/* * Automatic Volume name creation using the LabelFormat * * The media record must have the PoolId filled in when * calling this routine. */ bool newVolume(JCR *jcr, MEDIA_DBR *mr) { POOL_DBR pr; memset(&pr, 0, sizeof(pr)); /* See if we can create a new Volume */ db_lock(jcr->db); pr.PoolId = mr->PoolId; if (!db_get_pool_record(jcr, jcr->db, &pr)) { goto bail_out; } if (pr.MaxVols == 0 || pr.NumVols < pr.MaxVols) { memset(mr, 0, sizeof(MEDIA_DBR)); set_pool_dbr_defaults_in_media_dbr(mr, &pr); jcr->VolumeName[0] = 0; bstrncpy(mr->MediaType, jcr->wstore->media_type, sizeof(mr->MediaType)); generate_job_event(jcr, "NewVolume"); /* return bool */ generate_plugin_event(jcr, bEventNewVolume); /* return void... */ if (jcr->VolumeName[0] && is_volume_name_legal(NULL, jcr->VolumeName)) { bstrncpy(mr->VolumeName, jcr->VolumeName, sizeof(mr->VolumeName)); /* Check for special characters */ } else if (pr.LabelFormat[0] && pr.LabelFormat[0] != '*') { if (is_volume_name_legal(NULL, pr.LabelFormat)) { /* No special characters, so apply simple algorithm */ if (!create_simple_name(jcr, mr, &pr)) { goto bail_out; } } else { /* try full substitution */ /* Found special characters, so try substitution */ if (!perform_full_name_substitution(jcr, mr, &pr)) { goto bail_out; } if (!is_volume_name_legal(NULL, mr->VolumeName)) { Jmsg(jcr, M_ERROR, 0, _("Illegal character in Volume name \"%s\"\n"), mr->VolumeName); goto bail_out; } } } else { goto bail_out; } pr.NumVols++; mr->Enabled = 1; if (db_create_media_record(jcr, jcr->db, mr) && db_update_pool_record(jcr, jcr->db, &pr)) { db_unlock(jcr->db); Jmsg(jcr, M_INFO, 0, _("Created new Volume \"%s\" in catalog.\n"), mr->VolumeName); Dmsg1(90, "Created new Volume=%s\n", mr->VolumeName); return true; } else { Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); } } bail_out: db_unlock(jcr->db); return false; }
bool DEVICE::scan_dir_for_volume(DCR *dcr) { DIR* dp; struct dirent *entry, *result; int name_max; char *mount_point; VOLUME_CAT_INFO dcrVolCatInfo, devVolCatInfo; char VolumeName[MAX_NAME_LENGTH]; struct stat statp; bool found = false; POOL_MEM fname(PM_FNAME); bool need_slash = false; int len; dcrVolCatInfo = dcr->VolCatInfo; /* structure assignment */ devVolCatInfo = VolCatInfo; /* structure assignment */ bstrncpy(VolumeName, dcr->VolumeName, sizeof(VolumeName)); name_max = pathconf(".", _PC_NAME_MAX); if (name_max < 1024) { name_max = 1024; } if (device->mount_point) { mount_point = device->mount_point; } else { mount_point = device->device_name; } if (!(dp = opendir(mount_point))) { berrno be; dev_errno = errno; Dmsg3(29, "scan_dir_for_vol: failed to open dir %s (dev=%s), ERR=%s\n", mount_point, print_name(), be.bstrerror()); goto get_out; } len = strlen(mount_point); if (len > 0) { need_slash = !IsPathSeparator(mount_point[len - 1]); } entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000); for ( ;; ) { if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) { dev_errno = EIO; Dmsg2(129, "scan_dir_for_vol: failed to find suitable file in dir %s (dev=%s)\n", mount_point, print_name()); break; } if (strcmp(result->d_name, ".") == 0 || strcmp(result->d_name, "..") == 0) { continue; } if (!is_volume_name_legal(result->d_name)) { continue; } pm_strcpy(fname, mount_point); if (need_slash) { pm_strcat(fname, "/"); } pm_strcat(fname, result->d_name); if (lstat(fname.c_str(), &statp) != 0 || !S_ISREG(statp.st_mode)) { continue; /* ignore directories & special files */ } /* * OK, we got a different volume mounted. First save the * requested Volume info (dcr) structure, then query if * this volume is really OK. If not, put back the desired * volume name, mark it not in changer and continue. */ /* Check if this is a valid Volume in the pool */ bstrncpy(dcr->VolumeName, result->d_name, sizeof(dcr->VolumeName)); if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_WRITE)) { continue; } /* This was not the volume we expected, but it is OK with * the Director, so use it. */ VolCatInfo = dcr->VolCatInfo; /* structure assignment */ found = true; break; /* got a Volume */ } free(entry); closedir(dp); get_out: if (!found) { /* Restore VolumeName we really wanted */ bstrncpy(dcr->VolumeName, VolumeName, sizeof(dcr->VolumeName)); dcr->VolCatInfo = dcrVolCatInfo; /* structure assignment */ VolCatInfo = devVolCatInfo; /* structure assignment */ } Dsm_check(100); return found; }
/* * We get the slot list from the Storage daemon. * If listall is set we run an 'autochanger listall' cmd * otherwise an 'autochanger list' cmd * If scan is set and listall is not, we return all slots found, * otherwise, we return only slots with valid barcodes (Volume names) * * Input (output of mxt-changer list): * * 0:vol2 Slot num:Volume Name * * Input (output of mxt-changer listall): * * Drive content: D:Drive num:F:Slot loaded:Volume Name * D:0:F:2:vol2 or D:Drive num:E * D:1:F:42:vol42 * D:3:E * * Slot content: * S:1:F:vol1 S:Slot num:F:Volume Name * S:2:E or S:Slot num:E * S:3:F:vol4 * * Import/Export tray slots: * I:10:F:vol10 I:Slot num:F:Volume Name * I:11:E or I:Slot num:E * I:12:F:vol40 * * If a drive is loaded, the slot *should* be empty */ dlist *get_vol_list_from_SD(UAContext *ua, STORERES *store, bool listall, bool scan) { int nr_fields; char *bp; char dev_name[MAX_NAME_LENGTH]; char *field1, *field2, *field3, *field4, *field5; vol_list_t *vl = NULL; dlist *vol_list; BSOCK *sd = NULL; if (!(sd = open_sd_bsock(ua))) { return NULL; } bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); /* * Ask for autochanger list of volumes */ if (listall) { sd->fsend(changerlistallcmd , dev_name); } else { sd->fsend(changerlistcmd, dev_name); } vol_list = New(dlist(vl, &vl->link)); /* * Read and organize list of Volumes */ while (bnet_recv(sd) >= 0) { strip_trailing_junk(sd->msg); /* * Check for returned SD messages */ if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) && B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) && sd->msg[4] == ' ') { ua->send_msg("%s\n", sd->msg); /* pass them on to user */ continue; } /* * Parse the message. list gives max 2 fields listall max 5. * We always make sure all fields are initialized to either * a value or NULL. * * For autochanger list the following mapping is used: * - field1 == slotnr * - field2 == volumename * * For autochanger listall the following mapping is used: * - field1 == type * - field2 == slotnr * - field3 == content (E for Empty, F for Full) * - field4 == loaded (loaded slot if type == D) * - field4 == volumename (if type == S or I) * - field5 == volumename (if type == D) */ field1 = sd->msg; field2 = strchr(sd->msg, ':'); if (field2) { *field2++ = '\0'; if (listall) { field3 = strchr(field2, ':'); if (field3) { *field3++ = '\0'; field4 = strchr(field3, ':'); if (field4) { *field4++ = '\0'; field5 = strchr(field4, ':'); if (field5) { *field5++ = '\0'; nr_fields = 5; } else { nr_fields = 4; } } else { nr_fields = 3; field5 = NULL; } } else { nr_fields = 2; field4 = NULL; field5 = NULL; } } else { nr_fields = 2; field3 = NULL; field4 = NULL; field5 = NULL; } } else { nr_fields = 1; field3 = NULL; field4 = NULL; field5 = NULL; } /* * See if this is a parsable string from either list or listall * e.g. at least f1:f2 */ if (!field1 && !field2) { goto parse_error; } vl = (vol_list_t *)malloc(sizeof(vol_list_t)); memset(vl, 0, sizeof(vol_list_t)); if (scan && !listall) { /* * Scanning -- require only valid slot */ vl->Slot = atoi(field1); if (vl->Slot <= 0) { ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg); free(vl); continue; } vl->Type = slot_type_normal; if (strlen(field2) > 0) { vl->Content = slot_content_full; vl->VolName = bstrdup(field2); } else { vl->Content = slot_content_empty; } vl->Index = INDEX_SLOT_OFFSET + vl->Slot; } else if (!listall) { /* * Not scanning and not listall. */ if (strlen(field2) == 0) { free(vl); continue; } if (!is_an_integer(field1) || (vl->Slot = atoi(field1)) <= 0) { ua->error_msg(_("Invalid Slot number: %s\n"), field1); free(vl); continue; } if (!is_volume_name_legal(ua, field2)) { ua->error_msg(_("Invalid Volume name: %s\n"), field2); free(vl); continue; } vl->Type = slot_type_normal; vl->Content = slot_content_full; vl->VolName = bstrdup(field2); vl->Index = INDEX_SLOT_OFFSET + vl->Slot; } else { /* * Listall. */ if (!field3) { goto parse_error; } switch (*field1) { case 'D': vl->Type = slot_type_drive; break; case 'S': vl->Type = slot_type_normal; break; case 'I': vl->Type = slot_type_import; break; default: vl->Type = slot_type_unknown; break; } /* * For drives the Slot is the actual drive number. * For any other type its the actual slot number. */ switch (vl->Type) { case slot_type_drive: if (!is_an_integer(field2) || (vl->Slot = atoi(field2)) < 0) { ua->error_msg(_("Invalid Drive number: %s\n"), field2); free(vl); continue; } vl->Index = INDEX_DRIVE_OFFSET + vl->Slot; if (vl->Index >= INDEX_MAX_DRIVES) { ua->error_msg(_("Drive number %d greater then INDEX_MAX_DRIVES(%d) please increase define\n"), vl->Slot, INDEX_MAX_DRIVES); free(vl); continue; } break; default: if (!is_an_integer(field2) || (vl->Slot = atoi(field2)) <= 0) { ua->error_msg(_("Invalid Slot number: %s\n"), field2); free(vl); continue; } vl->Index = INDEX_SLOT_OFFSET + vl->Slot; break; } switch (*field3) { case 'E': vl->Content = slot_content_empty; break; case 'F': vl->Content = slot_content_full; switch (vl->Type) { case slot_type_normal: case slot_type_import: if (field4) { vl->VolName = bstrdup(field4); } break; case slot_type_drive: if (field4) { vl->Loaded = atoi(field4); } if (field5) { vl->VolName = bstrdup(field5); } break; default: break; } break; default: vl->Content = slot_content_unknown; break; } } if (vl->VolName) { Dmsg6(100, "Add index = %d slot=%d loaded=%d type=%d content=%d Vol=%s to SD list.\n", vl->Index, vl->Slot, vl->Loaded, vl->Type, vl->Content, NPRT(vl->VolName)); } else { Dmsg5(100, "Add index = %d slot=%d loaded=%d type=%d content=%d Vol=NULL to SD list.\n", vl->Index, vl->Slot, vl->Loaded, vl->Type, vl->Content); } vol_list->binary_insert(vl, compare_vol_list_entry); continue; parse_error: /* * We encountered a parse error, see how many replacements * we done of ':' with '\0' by looking at the nr_fields * variable and undo those. Number of undo's are nr_fields - 1 */ while (nr_fields > 1 && (bp = strchr(sd->msg, '\0')) != NULL) { *bp = ':'; nr_fields--; } ua->error_msg(_("Illegal output from autochanger %s: %s\n"), (listall) ? _("listall") : _("list"), sd->msg); free(vl); continue; } close_sd_bsock(ua); if (vol_list->size() == 0) { delete vol_list; vol_list = NULL; } return vol_list; }
/* * Common routine for both label and relabel */ static int do_label(UAContext *ua, const char *cmd, bool relabel) { USTORERES store; BSOCK *sd; char dev_name[MAX_NAME_LENGTH]; MEDIA_DBR mr, omr; POOL_DBR pr; bool print_reminder = true; bool label_barcodes = false; bool label_encrypt = false; int ok = FALSE; int i, j; int drive; bool media_record_exists = false; static const char *barcode_keywords[] = { "barcode", "barcodes", NULL }; memset(&pr, 0, sizeof(pr)); if (!open_client_db(ua)) { return 1; } /* * Look for one of the barcode keywords */ if (!relabel && (i = find_arg_keyword(ua, barcode_keywords)) >= 0) { /* * Now find the keyword in the list */ if ((j = find_arg(ua, barcode_keywords[i])) > 0) { *ua->argk[j] = 0; /* zap barcode keyword */ } label_barcodes = true; } /* * Look for the encrypt keyword */ if ((i = find_arg(ua, "encrypt")) > 0) { *ua->argk[i] = 0; /* zap encrypt keyword */ label_encrypt = true; } store.store = get_storage_resource(ua, true, label_barcodes); if (!store.store) { return 1; } switch (store.store->Protocol) { case APT_NDMPV2: case APT_NDMPV3: case APT_NDMPV4: /* * See if the user selected a NDMP storage device but its * handled by a native Bareos storage daemon e.g. we have * a paired_storage pointer. */ if (store.store->paired_storage) { store.store = store.store->paired_storage; } else { ua->warning_msg(_("Storage has non-native protocol.\n")); return 1; } break; default: break; } pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); drive = get_storage_drive(ua, store.store); if (label_barcodes) { label_from_barcodes(ua, drive, label_encrypt); return 1; } /* * If relabel get name of Volume to relabel */ if (relabel) { /* * Check for oldvolume=name */ i = find_arg_with_value(ua, "oldvolume"); if (i >= 0) { bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName)); if (db_get_media_record(ua->jcr, ua->db, &omr)) { goto checkVol; } ua->error_msg("%s", db_strerror(ua->db)); } /* * No keyword or Vol not found, ask user to select */ if (!select_media_dbr(ua, &omr)) { return 1; } /* * Require Volume to be Purged or Recycled */ checkVol: if (!bstrcmp(omr.VolStatus, "Purged") && !bstrcmp(omr.VolStatus, "Recycle")) { ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"), omr.VolumeName, omr.VolStatus); return 1; } } /* * Check for volume=NewVolume */ i = find_arg_with_value(ua, "volume"); if (i >= 0) { pm_strcpy(ua->cmd, ua->argv[i]); goto checkName; } /* * Get a new Volume name */ for ( ;; ) { media_record_exists = false; if (!get_cmd(ua, _("Enter new Volume name: "))) { return 1; } checkName: if (!is_volume_name_legal(ua, ua->cmd)) { continue; } /* * Search by Media name so set VolumeName and clear MediaId. */ mr.MediaId = 0; bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName)); /* * If VolBytes are zero the Volume is not labeled */ if (db_get_media_record(ua->jcr, ua->db, &mr)) { if (mr.VolBytes != 0) { ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"), mr.VolumeName); continue; } media_record_exists = true; } break; /* Got it */ } /* * If autochanger, request slot */ i = find_arg_with_value(ua, "slot"); if (i >= 0) { mr.Slot = atoi(ua->argv[i]); if (mr.Slot < 0) { mr.Slot = 0; } mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ } else if (store.store->autochanger) { if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) { return 1; } mr.Slot = ua->pint32_val; if (mr.Slot < 0) { mr.Slot = 0; } mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ } set_storageid_in_mr(store.store, &mr); bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType)); /* * Must select Pool if not already done */ if (pr.PoolId == 0) { memset(&pr, 0, sizeof(pr)); if (!select_pool_dbr(ua, &pr)) { return 1; } } /* * See if we need to generate a new passphrase for hardware encryption. */ if (label_encrypt) { ua->info_msg(_("Generating new hardware encryption key\n")); if (!generate_new_encryption_key(ua, &mr)) { return 1; } } ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive); if (ok) { sd = ua->jcr->store_bsock; if (relabel) { /* * Delete the old media record */ if (!db_delete_media_record(ua->jcr, ua->db, &omr)) { ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"), omr.VolumeName, db_strerror(ua->db)); } else { ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"), omr.VolumeName); /* * Update the number of Volumes in the pool */ pr.NumVols--; if (!db_update_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); } } } if (ua->automount) { bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name)); ua->info_msg(_("Requesting to mount %s ...\n"), dev_name); bash_spaces(dev_name); sd->fsend("mount %s drive=%d", dev_name, drive); unbash_spaces(dev_name); /* * We use bget_dirmsg here and not bnet_recv because as part of * the mount request the stored can request catalog information for * any plugin who listens to the bsdEventLabelVerified event. * As we don't want to loose any non protocol data e.g. errors * without a 3xxx prefix we set the allow_any_message of * bget_dirmsg to true and as such is behaves like a normal * bnet_recv for any non protocol messages. */ while (bget_dirmsg(sd, true) >= 0) { ua->send_msg("%s", sd->msg); /* * Here we can get * 3001 OK mount. Device=xxx or * 3001 Mounted Volume vvvv * 3002 Device "DVD-Writer" (/dev/hdc) is mounted. * 3906 is cannot mount non-tape * So for those, no need to print a reminder */ if (bstrncmp(sd->msg, "3001 ", 5) || bstrncmp(sd->msg, "3002 ", 5) || bstrncmp(sd->msg, "3906 ", 5)) { print_reminder = false; } } } } if (print_reminder) { ua->info_msg(_("Do not forget to mount the drive!!!\n")); } close_sd_bsock(ua); return 1; }
/* Returns: 0 for OK * -1 for error */ int job_setattr(PyObject *self, char *attrname, PyObject *value) { JCR *jcr; bool found = false; char *strval = NULL; int intval = 0; int i; Dmsg2(100, "In job_setattr=%s val=%p.\n", attrname, value); if (value == NULL) { /* Cannot delete variables */ goto bail_out; } jcr = get_jcr_from_PyObject(self); if (!jcr) { goto bail_out; } /* Find attribute name in list */ for (i=0; setvars[i].name; i++) { if (strcmp(setvars[i].name, attrname) == 0) { found = true; break; } } if (!found) { goto bail_out; } /* Get argument value */ if (setvars[i].fmt != NULL) { switch (setvars[i].fmt[0]) { case 's': if (!PyArg_Parse(value, (char *)setvars[i].fmt, &strval)) { PyErr_SetString(PyExc_TypeError, _("Read-only attribute")); return -1; } break; case 'i': if (!PyArg_Parse(value, (char *)setvars[i].fmt, &intval)) { PyErr_SetString(PyExc_TypeError, _("Read-only attribute")); return -1; } break; } } switch (i) { case 0: /* JobReport */ Jmsg(jcr, M_INFO, 0, "%s", strval); return 0; case 1: /* VolumeName */ /* Make sure VolumeName is valid and we are in VolumeName event */ if (strcmp("NewVolume", jcr->event) == 0 && is_volume_name_legal(NULL, strval)) { pm_strcpy(jcr->VolumeName, strval); Dmsg1(100, "Set Vol=%s\n", strval); return 0; } else { jcr->VolumeName[0] = 0; } break; case 2: /* Priority */ Dmsg1(000, "Set priority=%d\n", intval); if (intval >= 1 && intval <= 100) { jcr->JobPriority = intval; } else { PyErr_SetString(PyExc_ValueError, _("Priority must be 1-100")); return -1; } case 3: /* Job Level */ if (strcmp("JobInit", jcr->event) != 0) { PyErr_SetString(PyExc_RuntimeError, _("Job Level can be set only during JobInit")); return -1; } if (strval != NULL) { for (i=0; joblevels[i].level_name; i++) { if (strcmp(strval, joblevels[i].level_name) == 0) { if (joblevels[i].job_type == jcr->getJobType()) { jcr->setJobLevel(joblevels[i].level); jcr->jr.JobLevel = jcr->getJobLevel(); return 0; } } } } PyErr_SetString(PyExc_ValueError, _("Bad JobLevel string")); return -1; } bail_out: PyErr_SetString(PyExc_AttributeError, attrname); return -1; }
/* * We get the slot list from the Storage daemon. * If scan is set, we return all slots found, * otherwise, we return only slots with valid barcodes (Volume names) */ static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan) { STORE *store = ua->jcr->wstore; char dev_name[MAX_NAME_LENGTH]; BSOCK *sd; vol_list_t *vl; vol_list_t *vol_list = NULL; if (!(sd=open_sd_bsock(ua))) { return NULL; } bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); /* Ask for autochanger list of volumes */ bnet_fsend(sd, NT_("autochanger list %s \n"), dev_name); /* Read and organize list of Volumes */ while (bnet_recv(sd) >= 0) { char *p; int Slot; strip_trailing_junk(sd->msg); /* Check for returned SD messages */ if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) && B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) && sd->msg[4] == ' ') { ua->send_msg("%s\n", sd->msg); /* pass them on to user */ continue; } /* Validate Slot: if scanning, otherwise Slot:Barcode */ p = strchr(sd->msg, ':'); if (scan && p) { /* Scanning -- require only valid slot */ Slot = atoi(sd->msg); if (Slot <= 0) { p--; *p = ':'; ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg); continue; } } else { /* Not scanning */ if (p && strlen(p) > 1) { *p++ = 0; if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) { p--; *p = ':'; ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg); continue; } } else { continue; } if (!is_volume_name_legal(ua, p)) { p--; *p = ':'; ua->error_msg(_("Invalid Volume name: %s\n"), sd->msg); continue; } } /* Add Slot and VolumeName to list */ vl = (vol_list_t *)malloc(sizeof(vol_list_t)); vl->Slot = Slot; if (p) { if (*p == ':') { p++; /* skip separator */ } vl->VolName = bstrdup(p); } else { vl->VolName = NULL; } Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName)); if (!vol_list) { vl->next = vol_list; vol_list = vl; } else { /* Add new entry to end of list */ for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) { if (!tvl->next) { tvl->next = vl; vl->next = NULL; break; } } } } close_sd_bsock(ua); return vol_list; }
/* * Common routine for both label and relabel */ static int do_label(UAContext *ua, const char *cmd, int relabel) { USTORE store; BSOCK *sd; char dev_name[MAX_NAME_LENGTH]; MEDIA_DBR mr, omr; POOL_DBR pr; bool print_reminder = true; bool label_barcodes = false; int ok = FALSE; int i, j; int drive; bool media_record_exists = false; static const char *barcode_keyword[] = { "barcode", "barcodes", NULL}; memset(&pr, 0, sizeof(pr)); if (!open_client_db(ua)) { return 1; } /* Look for one of the barcode keywords */ if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) { /* Now find the keyword in the list */ if ((j = find_arg(ua, barcode_keyword[i])) > 0) { *ua->argk[j] = 0; /* zap barcode keyword */ } label_barcodes = true; } store.store = get_storage_resource(ua, true/*use default*/); if (!store.store) { return 1; } pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); drive = get_storage_drive(ua, store.store); if (label_barcodes) { label_from_barcodes(ua, drive); return 1; } /* If relabel get name of Volume to relabel */ if (relabel) { /* Check for oldvolume=name */ i = find_arg_with_value(ua, "oldvolume"); if (i >= 0) { memset(&omr, 0, sizeof(omr)); bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName)); if (db_get_media_record(ua->jcr, ua->db, &omr)) { goto checkVol; } ua->error_msg("%s", db_strerror(ua->db)); } /* No keyword or Vol not found, ask user to select */ if (!select_media_dbr(ua, &omr)) { return 1; } /* Require Volume to be Purged or Recycled */ checkVol: if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) { ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"), omr.VolumeName, omr.VolStatus); return 1; } } /* Check for volume=NewVolume */ i = find_arg_with_value(ua, "volume"); if (i >= 0) { pm_strcpy(ua->cmd, ua->argv[i]); goto checkName; } /* Get a new Volume name */ for ( ;; ) { media_record_exists = false; if (!get_cmd(ua, _("Enter new Volume name: "))) { return 1; } checkName: if (!is_volume_name_legal(ua, ua->cmd)) { continue; } memset(&mr, 0, sizeof(mr)); bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName)); /* If VolBytes are zero the Volume is not labeled */ if (db_get_media_record(ua->jcr, ua->db, &mr)) { if (mr.VolBytes != 0) { ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"), mr.VolumeName); continue; } media_record_exists = true; } break; /* Got it */ } /* If autochanger, request slot */ i = find_arg_with_value(ua, "slot"); if (i >= 0) { mr.Slot = atoi(ua->argv[i]); if (mr.Slot < 0) { mr.Slot = 0; } mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ } else if (store.store->autochanger) { if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) { return 1; } mr.Slot = ua->pint32_val; if (mr.Slot < 0) { mr.Slot = 0; } mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ } mr.StorageId = store.store->StorageId; bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType)); /* Must select Pool if not already done */ if (pr.PoolId == 0) { memset(&pr, 0, sizeof(pr)); if (!select_pool_dbr(ua, &pr)) { return 1; } } ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive); if (ok) { sd = ua->jcr->store_bsock; if (relabel) { /* Delete the old media record */ if (!db_delete_media_record(ua->jcr, ua->db, &omr)) { ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"), omr.VolumeName, db_strerror(ua->db)); } else { ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"), omr.VolumeName); /* Update the number of Volumes in the pool */ pr.NumVols--; if (!db_update_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); } } } if (ua->automount) { bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name)); ua->info_msg(_("Requesting to mount %s ...\n"), dev_name); bash_spaces(dev_name); bnet_fsend(sd, "mount %s drive=%d", dev_name, drive); unbash_spaces(dev_name); while (bnet_recv(sd) >= 0) { ua->send_msg("%s", sd->msg); /* Here we can get * 3001 OK mount. Device=xxx or * 3001 Mounted Volume vvvv * 3002 Device "DVD-Writer" (/dev/hdc) is mounted. * 3906 is cannot mount non-tape * So for those, no need to print a reminder */ if (strncmp(sd->msg, "3001 ", 5) == 0 || strncmp(sd->msg, "3002 ", 5) == 0 || strncmp(sd->msg, "3906 ", 5) == 0) { print_reminder = false; } } } } if (print_reminder) { ua->info_msg(_("Do not forget to mount the drive!!!\n")); } close_sd_bsock(ua); return 1; }