static void do_storage_cmd(UAContext *ua, STORERES *store, const char *cmd) { BSOCK *sd; JCR *jcr = ua->jcr; USTORERES lstore; lstore.store = store; pm_strcpy(lstore.store_source, _("unknown source")); set_wstorage(jcr, &lstore); if (!(sd = open_sd_bsock(ua))) { ua->error_msg(_("Could not open SD socket.\n")); return; } Dmsg0(120, _("Connected to storage daemon\n")); sd = jcr->store_bsock; sd->fsend("%s", cmd); if (sd->recv() >= 0) { ua->send_msg("%s", sd->msg); } close_sd_bsock(ua); return; }
/* * We get the number of drives in the changer from the SD */ int get_num_drives_from_SD(UAContext *ua) { STORERES *store = ua->jcr->res.wstore; char dev_name[MAX_NAME_LENGTH]; BSOCK *sd; int drives = 0; if (!(sd = open_sd_bsock(ua))) { return 0; } bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); /* * Ask for autochanger number of drives */ sd->fsend(changerdrivescmd, dev_name); while (sd->recv() >= 0) { if (sscanf(sd->msg, changerdrivesresponse, &drives) == 1) { break; } else { ua->send_msg("%s", sd->msg); } } close_sd_bsock(ua); // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives); return drives; }
static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive) { STORE *store = ua->jcr->wstore; BSOCK *sd; char dev_name[MAX_NAME_LENGTH]; char *VolName = NULL; int rtn_slot; if (!(sd=open_sd_bsock(ua))) { ua->error_msg(_("Could not open SD socket.\n")); return NULL; } bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); /* Ask for autochanger list of volumes */ sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive); Dmsg1(100, "Sent: %s", sd->msg); /* Get Volume name in this Slot */ while (sd->recv() >= 0) { ua->send_msg("%s", sd->msg); Dmsg1(100, "Got: %s", sd->msg); if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) { VolName = (char *)malloc(sd->msglen); if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) { break; } free(VolName); VolName = NULL; } } close_sd_bsock(ua); Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName)); return VolName; }
/* * We get the number of slots in the changer from the SD */ static int get_num_slots_from_SD(UAContext *ua) { STORE *store = ua->jcr->wstore; char dev_name[MAX_NAME_LENGTH]; BSOCK *sd; int slots = 0; if (!(sd=open_sd_bsock(ua))) { return 0; } bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); /* Ask for autochanger number of slots */ sd->fsend(NT_("autochanger slots %s\n"), dev_name); while (sd->recv() >= 0) { if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) { break; } else { ua->send_msg("%s", sd->msg); } } close_sd_bsock(ua); ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots); return slots; }
/* * Ask the autochanger to move a volume from one slot to an other. * You have to update the database slots yourself afterwards. */ bool transfer_volume(UAContext *ua, STORERES *store, int src_slot, int dst_slot) { BSOCK *sd = NULL; bool retval = true; char dev_name[MAX_NAME_LENGTH]; if (!(sd = open_sd_bsock(ua))) { return false; } bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); /* * Ask for autochanger transfer of volumes */ sd->fsend(changertransfercmd, dev_name, src_slot, dst_slot); 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] == ' ') { /* * See if this is a failure msg. */ if (sd->msg[0] == '3' && sd->msg[0] == '9') retval = false; ua->send_msg("%s\n", sd->msg); /* pass them on to user */ continue; } ua->send_msg("%s\n", sd->msg); /* pass them on to user */ } close_sd_bsock(ua); return retval; }
/* * We get the volume name from the SD */ char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive) { BSOCK *sd; STORERES *store = ua->jcr->res.wstore; char dev_name[MAX_NAME_LENGTH]; char *VolName = NULL; int rtn_slot; if (!(sd = open_sd_bsock(ua))) { ua->error_msg(_("Could not open SD socket.\n")); return NULL; } bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); /* * Ask storage daemon to read the label of the volume in a * specific slot of the autochanger using the drive number given. * This could change the loaded volume in the drive. */ sd->fsend(readlabelcmd, dev_name, Slot, drive); Dmsg1(100, "Sent: %s", sd->msg); /* * Get Volume name in this Slot */ while (sd->recv() >= 0) { ua->send_msg("%s", sd->msg); Dmsg1(100, "Got: %s", sd->msg); if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) { VolName = (char *)malloc(sd->msglen); if (sscanf(sd->msg, readlabelresponse, VolName, &rtn_slot) == 2) { break; } free(VolName); VolName = NULL; } } close_sd_bsock(ua); Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName)); return VolName; }
/* * Ask the autochanger to perform a mount, umount or release operation. */ bool do_autochanger_volume_operation(UAContext *ua, STORERES *store, const char *operation, int drive, int slot) { BSOCK *sd = NULL; bool retval = true; char dev_name[MAX_NAME_LENGTH]; if (!(sd = open_sd_bsock(ua))) { return false; } bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); if (slot > 0) { sd->fsend(changervolopslotcmd, operation, dev_name, drive, slot); } else { sd->fsend(changervolopcmd, operation, dev_name, drive); } /* * 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); } close_sd_bsock(ua); return retval; }
/* * 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; }
/* * Request SD to send us the slot:barcodes, then wiffle * through them all labeling them. */ static void label_from_barcodes(UAContext *ua, int drive, bool label_encrypt) { STORERES *store = ua->jcr->res.wstore; POOL_DBR pr; MEDIA_DBR mr, omr; vol_list_t *vl; dlist *vol_list = NULL; bool media_record_exists; char *slot_list; int max_slots; max_slots = get_num_slots_from_SD(ua); if (max_slots <= 0) { ua->warning_msg(_("No slots in changer to scan.\n")); return; } slot_list = (char *)malloc(nbytes_for_bits(max_slots)); clear_all_bits(max_slots, slot_list); if (!get_user_slot_list(ua, slot_list, "slots", max_slots)) { goto bail_out; } vol_list = get_vol_list_from_SD(ua, store, false /* no listall */ , false /*no scan*/); if (!vol_list) { ua->warning_msg(_("No Volumes found to label, or no barcodes.\n")); goto bail_out; } /* * Display list of Volumes and ask if he really wants to proceed */ ua->send_msg(_("The following Volumes will be labeled:\n" "Slot Volume\n" "==============\n")); foreach_dlist(vl, vol_list) { if (!vl->VolName || !bit_is_set(vl->Slot - 1, slot_list)) { continue; } ua->send_msg("%4d %s\n", vl->Slot, vl->VolName); } if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) || (ua->pint32_val == 0)) { goto bail_out; } /* * Select a pool */ memset(&pr, 0, sizeof(pr)); if (!select_pool_dbr(ua, &pr)) { goto bail_out; } /* * Fire off the label requests */ foreach_dlist(vl, vol_list) { if (!vl->VolName || !bit_is_set(vl->Slot - 1, slot_list)) { continue; } mr.clear(); bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName)); media_record_exists = false; if (db_get_media_record(ua->jcr, ua->db, &mr)) { if (mr.VolBytes != 0) { ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"), vl->Slot, mr.VolumeName); mr.Slot = vl->Slot; mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ set_storageid_in_mr(store, &mr); if (!db_update_media_record(ua->jcr, ua->db, &mr)) { ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db)); } continue; } media_record_exists = true; } mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ set_storageid_in_mr(store, &mr); /* * Deal with creating cleaning tape here. Normal tapes created in send_label_request() below */ if (is_cleaning_tape(ua, &mr, &pr)) { if (media_record_exists) { /* we update it */ mr.VolBytes = 1; /* any bytes to indicate it exists */ bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus)); mr.MediaType[0] = 0; set_storageid_in_mr(store, &mr); if (!db_update_media_record(ua->jcr, ua->db, &mr)) { ua->error_msg("%s", db_strerror(ua->db)); } } else { /* create the media record */ if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) { ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols); goto bail_out; } set_pool_dbr_defaults_in_media_dbr(&mr, &pr); bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus)); mr.MediaType[0] = 0; set_storageid_in_mr(store, &mr); if (db_create_media_record(ua->jcr, ua->db, &mr)) { ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"), mr.VolumeName); pr.NumVols++; /* this is a bit suspect */ if (!db_update_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); } } else { ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db)); } } continue; /* done, go handle next volume */ } bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType)); /* * See if we need to generate a new passphrase for hardware encryption. */ if (label_encrypt) { if (!generate_new_encryption_key(ua, &mr)) { continue; } } mr.Slot = vl->Slot; send_label_request(ua, &mr, &omr, &pr, false, media_record_exists, drive); } bail_out: free(slot_list); if (vol_list) { free_vol_list(vol_list); } close_sd_bsock(ua); return; }
/* * 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; }
/* * Update Slots corresponding to Volumes in autochanger */ void update_slots(UAContext *ua) { USTORE store; vol_list_t *vl, *vol_list = NULL; MEDIA_DBR mr; char *slot_list; bool scan; int max_slots; int drive; int Enabled = 1; bool have_enabled; int i; if (!open_client_db(ua)) { return; } store.store = get_storage_resource(ua, true/*arg is storage*/); if (!store.store) { return; } pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); drive = get_storage_drive(ua, store.store); scan = find_arg(ua, NT_("scan")) >= 0; if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) { Enabled = get_enabled(ua, ua->argv[i]); if (Enabled < 0) { return; } have_enabled = true; } else { have_enabled = false; } max_slots = get_num_slots_from_SD(ua); Dmsg1(100, "max_slots=%d\n", max_slots); if (max_slots <= 0) { ua->warning_msg(_("No slots in changer to scan.\n")); return; } slot_list = (char *)malloc(max_slots+1); if (!get_user_slot_list(ua, slot_list, max_slots)) { free(slot_list); return; } vol_list = get_vol_list_from_SD(ua, scan); if (!vol_list) { ua->warning_msg(_("No Volumes found to label, or no barcodes.\n")); goto bail_out; } /* First zap out any InChanger with StorageId=0 */ db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL); /* Walk through the list updating the media records */ for (vl=vol_list; vl; vl=vl->next) { if (vl->Slot > max_slots) { ua->warning_msg(_("Slot %d greater than max %d ignored.\n"), vl->Slot, max_slots); continue; } /* Check if user wants us to look at this slot */ if (!slot_list[vl->Slot]) { Dmsg1(100, "Skipping slot=%d\n", vl->Slot); continue; } /* If scanning, we read the label rather than the barcode */ if (scan) { if (vl->VolName) { free(vl->VolName); vl->VolName = NULL; } vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive); Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot); } slot_list[vl->Slot] = 0; /* clear Slot */ memset(&mr, 0, sizeof(mr)); mr.Slot = vl->Slot; mr.InChanger = 1; mr.StorageId = store.store->StorageId; /* Set InChanger to zero for this Slot */ db_lock(ua->db); db_make_inchanger_unique(ua->jcr, ua->db, &mr); db_unlock(ua->db); if (!vl->VolName) { Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot); ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot); continue; } memset(&mr, 0, sizeof(mr)); bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName)); db_lock(ua->db); if (db_get_media_record(ua->jcr, ua->db, &mr)) { if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) { mr.Slot = vl->Slot; mr.InChanger = 1; mr.StorageId = store.store->StorageId; if (have_enabled) { mr.Enabled = Enabled; } if (!db_update_media_record(ua->jcr, ua->db, &mr)) { ua->error_msg("%s", db_strerror(ua->db)); } else { ua->info_msg(_( "Catalog record for Volume \"%s\" updated to reference slot %d.\n"), mr.VolumeName, mr.Slot); } } else { ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"), mr.VolumeName); } db_unlock(ua->db); continue; } else { ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"), mr.VolumeName, vl->Slot); } db_unlock(ua->db); } memset(&mr, 0, sizeof(mr)); mr.InChanger = 1; mr.StorageId = store.store->StorageId; db_lock(ua->db); for (int i=1; i <= max_slots; i++) { if (slot_list[i]) { mr.Slot = i; /* Set InChanger to zero for this Slot */ db_make_inchanger_unique(ua->jcr, ua->db, &mr); } } db_unlock(ua->db); bail_out: free_vol_list(vol_list); free(slot_list); close_sd_bsock(ua); return; }
/* * Print slots from AutoChanger */ void status_slots(UAContext *ua, STORE *store_r) { USTORE store; POOL_DBR pr; vol_list_t *vl, *vol_list = NULL; MEDIA_DBR mr; char *slot_list; int max_slots; int drive; int i=1; /* output format */ const char *slot_api_empty_format="%i|||||\n"; const char *slot_api_full_format="%i|%i|%s|%s|%s|%s|\n"; const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n"; if (!open_client_db(ua)) { return; } store.store = store_r; pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); drive = get_storage_drive(ua, store.store); max_slots = get_num_slots_from_SD(ua); if (max_slots <= 0) { ua->warning_msg(_("No slots in changer to scan.\n")); return; } slot_list = (char *)malloc(max_slots+1); if (!get_user_slot_list(ua, slot_list, max_slots)) { free(slot_list); return; } vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */); if (!vol_list) { ua->warning_msg(_("No Volumes found, or no barcodes.\n")); goto bail_out; } if (!ua->api) { ua->info_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n")); ua->info_msg(_("------+------------------+-----------+----------------------+--------------------|\n")); } /* Walk through the list getting the media records */ for (vl=vol_list; vl; vl=vl->next) { if (vl->Slot > max_slots) { ua->warning_msg(_("Slot %d greater than max %d ignored.\n"), vl->Slot, max_slots); continue; } /* Check if user wants us to look at this slot */ if (!slot_list[vl->Slot]) { Dmsg1(100, "Skipping slot=%d\n", vl->Slot); continue; } slot_list[vl->Slot] = 0; /* clear Slot */ if (!vl->VolName) { Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot); if (!ua->api) { ua->info_msg(slot_hformat, vl->Slot, '*', "?", "?", "?", "?"); } else { ua->info_msg(slot_api_empty_format, vl->Slot); } continue; } /* Hope that slots are ordered */ for (; i < vl->Slot; i++) { if (slot_list[i]) { if (!ua->api) { ua->info_msg(slot_hformat, i, ' ', "", "", "", ""); } else { ua->info_msg(slot_api_empty_format, i); } slot_list[i]=0; } } memset(&mr, 0, sizeof(mr)); bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName)); db_lock(ua->db); if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) { memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.PoolId; if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { strcpy(pr.Name, "?"); } if (!ua->api) { /* Print information */ ua->info_msg(slot_hformat, vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'), mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name); } else { ua->info_msg(slot_api_full_format, vl->Slot, mr.Slot, mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name); } db_unlock(ua->db); continue; } else { /* TODO: get information from catalog */ ua->info_msg(slot_hformat, vl->Slot, '*', mr.VolumeName, "?", "?", "?"); } db_unlock(ua->db); } /* Display the rest of the autochanger */ for (; i <= max_slots; i++) { if (slot_list[i]) { if (!ua->api) { ua->info_msg(slot_hformat, i, ' ', "", "", "", ""); } else { ua->info_msg(slot_api_empty_format, i); } slot_list[i]=0; } } bail_out: free_vol_list(vol_list); free(slot_list); close_sd_bsock(ua); return; }
/* * Update Slots corresponding to Volumes in autochanger */ static void update_slots(UAContext *ua) { USTORERES store; vol_list_t *vl; dlist *vol_list = NULL; MEDIA_DBR mr; char *slot_list; bool scan; int max_slots; int drive = -1; int Enabled = 1; bool have_enabled; int i; if (!open_client_db(ua)) { return; } store.store = get_storage_resource(ua, true, true); if (!store.store) { return; } pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); scan = find_arg(ua, NT_("scan")) >= 0; if (scan) { drive = get_storage_drive(ua, store.store); } if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) { Enabled = get_enabled(ua, ua->argv[i]); if (Enabled < 0) { return; } have_enabled = true; } else { have_enabled = false; } max_slots = get_num_slots_from_SD(ua); Dmsg1(100, "max_slots=%d\n", max_slots); if (max_slots <= 0) { ua->warning_msg(_("No slots in changer to scan.\n")); return; } slot_list = (char *)malloc(nbytes_for_bits(max_slots)); clear_all_bits(max_slots, slot_list); if (!get_user_slot_list(ua, slot_list, "slots", max_slots)) { free(slot_list); return; } vol_list = get_vol_list_from_SD(ua, store.store, false, scan); if (!vol_list) { ua->warning_msg(_("No Volumes found to update, or no barcodes.\n")); goto bail_out; } /* * First zap out any InChanger with StorageId=0 */ db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0"); /* * Walk through the list updating the media records */ memset(&mr, 0, sizeof(mr)); foreach_dlist(vl, vol_list) { if (vl->Slot > max_slots) { ua->warning_msg(_("Slot %d greater than max %d ignored.\n"), vl->Slot, max_slots); continue; } /* * Check if user wants us to look at this slot */ if (!bit_is_set(vl->Slot - 1, slot_list)) { Dmsg1(100, "Skipping slot=%d\n", vl->Slot); continue; } /* * If scanning, we read the label rather than the barcode */ if (scan) { if (vl->VolName) { free(vl->VolName); vl->VolName = NULL; } vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive); Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot); } clear_bit(vl->Slot - 1, slot_list); /* clear Slot */ set_storageid_in_mr(store.store, &mr); mr.Slot = vl->Slot; mr.InChanger = 1; mr.MediaId = 0; /* Get by VolumeName */ if (vl->VolName) { bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName)); } else { mr.VolumeName[0] = 0; } set_storageid_in_mr(store.store, &mr); Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n", mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId); db_lock(ua->db); /* * Set InChanger to zero for this Slot */ db_make_inchanger_unique(ua->jcr, ua->db, &mr); db_unlock(ua->db); Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n", mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId); if (!vl->VolName) { Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot); ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot); continue; } db_lock(ua->db); Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n", mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId); if (db_get_media_record(ua->jcr, ua->db, &mr)) { Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n", mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId); /* * If Slot, Inchanger, and StorageId have changed, update the Media record */ if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) { mr.Slot = vl->Slot; mr.InChanger = 1; if (have_enabled) { mr.Enabled = Enabled; } set_storageid_in_mr(store.store, &mr); if (!db_update_media_record(ua->jcr, ua->db, &mr)) { ua->error_msg("%s", db_strerror(ua->db)); } else { ua->info_msg(_("Catalog record for Volume \"%s\" updated to reference slot %d.\n"), mr.VolumeName, mr.Slot); } } else { ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"), mr.VolumeName); } } else { ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"), mr.VolumeName, vl->Slot); } db_unlock(ua->db); } memset(&mr, 0, sizeof(mr)); mr.InChanger = 1; set_storageid_in_mr(store.store, &mr); /* * Any slot not visited gets it Inchanger flag reset. */ db_lock(ua->db); for (i = 1; i <= max_slots; i++) { if (bit_is_set(i - 1, slot_list)) { /* * Set InChanger to zero for this Slot */ mr.Slot = i; db_make_inchanger_unique(ua->jcr, ua->db, &mr); } } db_unlock(ua->db); bail_out: if (vol_list) { free_vol_list(vol_list); } free(slot_list); close_sd_bsock(ua); return; }