2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Director -- Tape labeling commands
22 * Kern Sibbald, April MMIII
28 /* Slot list definition */
29 typedef struct s_vol_list {
30 struct s_vol_list *next;
36 /* Forward referenced functions */
37 static int do_label(UAContext *ua, const char *cmd, int relabel);
38 static void label_from_barcodes(UAContext *ua, int drive);
39 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
40 POOL_DBR *pr, int relabel, bool media_record_exits, int drive);
41 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan);
42 static void free_vol_list(vol_list_t *vol_list);
43 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr);
44 BSOCK *open_sd_bsock(UAContext *ua);
45 void close_sd_bsock(UAContext *ua);
46 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive);
47 static int get_num_slots_from_SD(UAContext *ua);
53 * label storage=xxx volume=vvv
55 int label_cmd(UAContext *ua, const char *cmd)
57 return do_label(ua, cmd, 0); /* standard label */
60 int relabel_cmd(UAContext *ua, const char *cmd)
62 return do_label(ua, cmd, 1); /* relabel tape */
65 static bool get_user_slot_list(UAContext *ua, char *slot_list, int num_slots)
70 /* slots are numbered 1 to num_slots */
71 for (int i=0; i <= num_slots; i++) {
74 i = find_arg_with_value(ua, "slots");
75 if (i == -1) { /* not found */
76 i = find_arg_with_value(ua, "slot");
79 /* scan slot list in ua->argv[i] */
83 strip_trailing_junk(ua->argv[i]);
84 for (p=ua->argv[i]; p && *p; p=e) {
91 h = strchr(p, '-'); /* range? */
93 msg = _("Negative numbers not permitted\n");
98 if (!is_an_integer(h)) {
99 msg = _("Range end is not integer.\n");
103 if (!is_an_integer(p)) {
104 msg = _("Range start is not an integer.\n");
110 msg = _("Range end not bigger than start.\n");
115 if (!is_an_integer(p)) {
116 msg = _("Input value is not an integer.\n");
121 if (beg <= 0 || end <= 0) {
122 msg = _("Values must be be greater than zero.\n");
125 if (end > num_slots) {
126 msg = _("Slot too large.\n");
129 for (i=beg; i<=end; i++) {
130 slot_list[i] = 1; /* Turn on specified range */
134 /* Turn everything on */
135 for (i=1; i <= num_slots; i++) {
139 if (debug_level >= 100) {
140 Dmsg0(100, "Slots turned on:\n");
141 for (i=1; i <= num_slots; i++) {
143 Dmsg1(100, "%d\n", i);
150 Dmsg1(100, "Problem with user selection ERR=%s\n", msg);
155 * Update Slots corresponding to Volumes in autochanger
157 void update_slots(UAContext *ua)
160 vol_list_t *vl, *vol_list = NULL;
171 if (!open_client_db(ua)) {
174 store.store = get_storage_resource(ua, true/*arg is storage*/);
178 pm_strcpy(store.store_source, _("Command input"));
179 set_wstorage(ua->jcr, &store);
180 drive = get_storage_drive(ua, store.store);
182 scan = find_arg(ua, NT_("scan")) >= 0;
183 if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
184 Enabled = get_enabled(ua, ua->argv[i]);
190 have_enabled = false;
193 max_slots = get_num_slots_from_SD(ua);
194 Dmsg1(100, "max_slots=%d\n", max_slots);
195 if (max_slots <= 0) {
196 ua->warning_msg(_("No slots in changer to scan.\n"));
199 slot_list = (char *)malloc(max_slots+1);
200 if (!get_user_slot_list(ua, slot_list, max_slots)) {
205 vol_list = get_vol_list_from_SD(ua, scan);
208 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
212 /* First zap out any InChanger with StorageId=0 */
213 db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);
215 /* Walk through the list updating the media records */
216 for (vl=vol_list; vl; vl=vl->next) {
217 if (vl->Slot > max_slots) {
218 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
219 vl->Slot, max_slots);
222 /* Check if user wants us to look at this slot */
223 if (!slot_list[vl->Slot]) {
224 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
227 /* If scanning, we read the label rather than the barcode */
233 vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
234 Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
236 slot_list[vl->Slot] = 0; /* clear Slot */
239 mr.MediaId = 0; /* Force using VolumeName */
241 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
243 mr.VolumeName[0] = 0;
245 set_storageid_in_mr(store.store, &mr);
246 Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
247 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
249 /* Set InChanger to zero for this Slot */
250 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
252 Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
253 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
255 Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
256 ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
260 Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
261 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
262 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
263 Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
264 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
265 /* If Slot, Inchanger, and StorageId have changed, update the Media record */
266 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
270 mr.Enabled = Enabled;
272 set_storageid_in_mr(store.store, &mr);
273 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
274 ua->error_msg("%s", db_strerror(ua->db));
277 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
278 mr.VolumeName, mr.Slot);
281 ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
287 ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
288 mr.VolumeName, vl->Slot);
294 set_storageid_in_mr(store.store, &mr);
296 for (int i=1; i <= max_slots; i++) {
299 /* Set InChanger to zero for this Slot */
300 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
307 free_vol_list(vol_list);
317 * Common routine for both label and relabel
319 static int do_label(UAContext *ua, const char *cmd, int relabel)
323 char dev_name[MAX_NAME_LENGTH];
326 bool print_reminder = true;
327 bool label_barcodes = false;
331 bool media_record_exists = false;
332 static const char *barcode_keyword[] = {
338 memset(&pr, 0, sizeof(pr));
339 if (!open_client_db(ua)) {
343 /* Look for one of the barcode keywords */
344 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
345 /* Now find the keyword in the list */
346 if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
347 *ua->argk[j] = 0; /* zap barcode keyword */
349 label_barcodes = true;
352 store.store = get_storage_resource(ua, true/*use default*/);
356 pm_strcpy(store.store_source, _("Command input"));
357 set_wstorage(ua->jcr, &store);
358 drive = get_storage_drive(ua, store.store);
360 if (label_barcodes) {
361 label_from_barcodes(ua, drive);
365 /* If relabel get name of Volume to relabel */
367 /* Check for oldvolume=name */
368 i = find_arg_with_value(ua, "oldvolume");
370 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
372 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
375 ua->error_msg("%s", db_strerror(ua->db));
377 /* No keyword or Vol not found, ask user to select */
378 if (!select_media_dbr(ua, &omr)) {
382 /* Require Volume to be Purged or Recycled */
384 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
385 ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
386 omr.VolumeName, omr.VolStatus);
391 /* Check for volume=NewVolume */
392 i = find_arg_with_value(ua, "volume");
394 pm_strcpy(ua->cmd, ua->argv[i]);
398 /* Get a new Volume name */
400 media_record_exists = false;
401 if (!get_cmd(ua, _("Enter new Volume name: "))) {
405 if (!is_volume_name_legal(ua, ua->cmd)) {
409 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
411 /* If VolBytes are zero the Volume is not labeled */
412 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
413 if (mr.VolBytes != 0) {
414 ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
418 media_record_exists = true;
423 /* If autochanger, request slot */
424 i = find_arg_with_value(ua, "slot");
426 mr.Slot = atoi(ua->argv[i]);
430 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
431 } else if (store.store->autochanger) {
432 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
435 mr.Slot = ua->pint32_val;
439 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
441 set_storageid_in_mr(store.store, &mr);
443 bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
445 /* Must select Pool if not already done */
446 if (pr.PoolId == 0) {
447 memset(&pr, 0, sizeof(pr));
448 if (!select_pool_dbr(ua, &pr)) {
453 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
456 sd = ua->jcr->store_bsock;
458 /* Delete the old media record */
459 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
460 ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
461 omr.VolumeName, db_strerror(ua->db));
463 ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"),
465 /* Update the number of Volumes in the pool */
467 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
468 ua->error_msg("%s", db_strerror(ua->db));
473 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
474 ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
475 bash_spaces(dev_name);
476 sd->fsend("mount %s drive=%d slot=%d", dev_name, drive, mr.Slot);
477 unbash_spaces(dev_name);
478 while (bget_dirmsg(sd) >= 0) {
479 ua->send_msg("%s", sd->msg);
481 * 3001 OK mount. Device=xxx or
482 * 3001 Mounted Volume vvvv
483 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
484 * 3906 is cannot mount non-tape
485 * So for those, no need to print a reminder
487 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
488 strncmp(sd->msg, "3002 ", 5) == 0 ||
489 strncmp(sd->msg, "3906 ", 5) == 0) {
490 print_reminder = false;
495 if (print_reminder) {
496 ua->info_msg(_("Do not forget to mount the drive!!!\n"));
504 * Request SD to send us the slot:barcodes, then wiffle
505 * through them all labeling them.
507 static void label_from_barcodes(UAContext *ua, int drive)
509 STORE *store = ua->jcr->wstore;
512 vol_list_t *vl, *vol_list = NULL;
513 bool media_record_exists;
518 max_slots = get_num_slots_from_SD(ua);
519 if (max_slots <= 0) {
520 ua->warning_msg(_("No slots in changer to scan.\n"));
523 slot_list = (char *)malloc(max_slots+1);
524 if (!get_user_slot_list(ua, slot_list, max_slots)) {
528 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
531 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
535 /* Display list of Volumes and ask if he really wants to proceed */
536 ua->send_msg(_("The following Volumes will be labeled:\n"
538 "==============\n"));
539 for (vl=vol_list; vl; vl=vl->next) {
540 if (!vl->VolName || !slot_list[vl->Slot]) {
543 ua->send_msg("%4d %s\n", vl->Slot, vl->VolName);
545 if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
546 (ua->pint32_val == 0)) {
550 memset(&pr, 0, sizeof(pr));
551 if (!select_pool_dbr(ua, &pr)) {
555 /* Fire off the label requests */
556 for (vl=vol_list; vl; vl=vl->next) {
557 if (!vl->VolName || !slot_list[vl->Slot]) {
561 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
562 media_record_exists = false;
563 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
564 if (mr.VolBytes != 0) {
565 ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
566 vl->Slot, mr.VolumeName);
568 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
569 set_storageid_in_mr(store, &mr);
570 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
571 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
575 media_record_exists = true;
577 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
578 set_storageid_in_mr(store, &mr);
580 * Deal with creating cleaning tape here. Normal tapes created in
581 * send_label_request() below
583 if (is_cleaning_tape(ua, &mr, &pr)) {
584 if (media_record_exists) { /* we update it */
585 mr.VolBytes = 1; /* any bytes to indicate it exists */
586 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
588 set_storageid_in_mr(store, &mr);
589 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
590 ua->error_msg("%s", db_strerror(ua->db));
592 } else { /* create the media record */
593 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
594 ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
597 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
598 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
600 set_storageid_in_mr(store, &mr);
601 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
602 ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
604 pr.NumVols++; /* this is a bit suspect */
605 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
606 ua->error_msg("%s", db_strerror(ua->db));
609 ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
612 continue; /* done, go handle next volume */
614 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
617 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
623 free_vol_list(vol_list);
630 * Check if the Volume name has legal characters
631 * If ua is non-NULL send the message
633 bool is_volume_name_legal(UAContext *ua, const char *name)
637 const char *accept = ":.-_";
639 /* Restrict the characters permitted in the Volume name */
640 for (p=name; *p; p++) {
641 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
645 ua->error_msg(_("Illegal character \"%c\" in a volume name.\n"), *p);
650 if (len >= MAX_NAME_LENGTH) {
652 ua->error_msg(_("Volume name too long.\n"));
658 ua->error_msg(_("Volume name must be at least one character long.\n"));
666 * NOTE! This routine opens the SD socket but leaves it open
668 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
669 POOL_DBR *pr, int relabel, bool media_record_exists,
673 char dev_name[MAX_NAME_LENGTH];
675 uint64_t VolBytes = 0;
676 uint64_t VolABytes = 0;
677 uint32_t VolType = 0;
679 if (!(sd=open_sd_bsock(ua))) {
682 bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
683 bash_spaces(dev_name);
684 bash_spaces(mr->VolumeName);
685 bash_spaces(mr->MediaType);
686 bash_spaces(pr->Name);
688 bash_spaces(omr->VolumeName);
689 sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
690 "MediaType=%s Slot=%d drive=%d",
691 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
692 mr->MediaType, mr->Slot, drive);
693 ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
694 omr->VolumeName, mr->VolumeName);
696 sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
698 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
700 ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"),
701 mr->VolumeName, mr->Slot);
702 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
703 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
706 while (bget_dirmsg(sd) >= 0) {
707 ua->send_msg("%s", sd->msg);
708 if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu VolABytes=%lld VolType=%d ",
709 &VolBytes, &VolABytes, &VolType) == 3) {
711 if (media_record_exists) { /* we update it */
712 mr->VolBytes = VolBytes;
713 mr->VolABytes = VolABytes;
714 mr->VolType = VolType;
715 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
716 set_storageid_in_mr(ua->jcr->wstore, mr);
717 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
718 ua->error_msg("%s", db_strerror(ua->db));
721 } else { /* create the media record */
722 set_pool_dbr_defaults_in_media_dbr(mr, pr);
723 mr->VolBytes = VolBytes;
724 mr->VolABytes = VolABytes;
725 mr->VolType = VolType;
726 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
728 set_storageid_in_mr(ua->jcr->wstore, mr);
729 if (db_create_media_record(ua->jcr, ua->db, mr)) {
730 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
731 mr->VolumeName, mr->Slot);
732 /* Update number of volumes in pool */
734 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
735 ua->error_msg("%s", db_strerror(ua->db));
738 ua->error_msg("%s", db_strerror(ua->db));
745 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
750 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
752 STORE *store = ua->jcr->wstore;
754 char dev_name[MAX_NAME_LENGTH];
755 char *VolName = NULL;
758 if (!(sd=open_sd_bsock(ua))) {
759 ua->error_msg(_("Could not open SD socket.\n"));
762 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
763 bash_spaces(dev_name);
764 /* Ask for autochanger list of volumes */
765 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
766 Dmsg1(100, "Sent: %s", sd->msg);
768 /* Get Volume name in this Slot */
769 while (sd->recv() >= 0) {
770 ua->send_msg("%s", sd->msg);
771 Dmsg1(100, "Got: %s", sd->msg);
772 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
773 VolName = (char *)malloc(sd->msglen);
774 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
782 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
787 * We get the slot list from the Storage daemon.
788 * If scan is set, we return all slots found,
789 * otherwise, we return only slots with valid barcodes (Volume names)
791 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
793 STORE *store = ua->jcr->wstore;
794 char dev_name[MAX_NAME_LENGTH];
797 vol_list_t *vol_list = NULL;
800 if (!(sd=open_sd_bsock(ua))) {
804 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
805 bash_spaces(dev_name);
806 /* Ask for autochanger list of volumes */
807 sd->fsend(NT_("autochanger list %s \n"), dev_name);
809 /* Read and organize list of Volumes */
810 while (sd->recv() >= 0) {
813 strip_trailing_junk(sd->msg);
815 /* Check for returned SD messages */
816 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
817 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
819 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
823 /* Validate Slot: if scanning, otherwise Slot:Barcode */
824 p = strchr(sd->msg, ':');
826 /* Scanning -- require only valid slot */
827 Slot = atoi(sd->msg);
831 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
836 if (p && strlen(p) > 1) {
838 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
841 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
847 if (!is_volume_name_legal(ua, p)) {
850 ua->error_msg(_("Invalid Volume name: %s. Volume skipped.\n"), sd->msg);
855 /* Add Slot and VolumeName to list */
856 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
860 p++; /* skip separator */
862 vl->VolName = bstrdup(p);
866 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
871 vol_list_t *prev=vol_list;
872 /* Add new entry to the right place in the list */
873 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
874 if (tvl->Slot > vl->Slot) {
875 /* no previous item, update vol_list directly */
876 if (prev == vol_list) {
880 } else { /* replace the previous pointer */
886 /* we are at the end */
900 static void free_vol_list(vol_list_t *vol_list)
905 for (vl=vol_list; vl; ) {
917 * We get the number of slots in the changer from the SD
919 static int get_num_slots_from_SD(UAContext *ua)
921 STORE *store = ua->jcr->wstore;
922 char dev_name[MAX_NAME_LENGTH];
927 if (!(sd=open_sd_bsock(ua))) {
931 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
932 bash_spaces(dev_name);
933 /* Ask for autochanger number of slots */
934 sd->fsend(NT_("autochanger slots %s\n"), dev_name);
936 while (sd->recv() >= 0) {
937 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
940 ua->send_msg("%s", sd->msg);
944 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
949 * We get the number of drives in the changer from the SD
951 int get_num_drives_from_SD(UAContext *ua)
953 STORE *store = ua->jcr->wstore;
954 char dev_name[MAX_NAME_LENGTH];
959 if (!(sd=open_sd_bsock(ua))) {
963 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
964 bash_spaces(dev_name);
965 /* Ask for autochanger number of slots */
966 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
968 while (sd->recv() >= 0) {
969 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
972 ua->send_msg("%s", sd->msg);
976 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
981 * Check if this is a cleaning tape by comparing the Volume name
982 * with the Cleaning Prefix. If they match, this is a cleaning
985 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
987 /* Find Pool resource */
988 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
989 if (!ua->jcr->pool) {
990 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
991 pr->Name, mr->VolumeName);
994 if (ua->jcr->pool->cleaning_prefix == NULL) {
997 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
998 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
999 strlen(ua->jcr->pool->cleaning_prefix),
1000 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1001 strlen(ua->jcr->pool->cleaning_prefix)));
1002 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1003 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1006 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1008 char ed1[50], ed2[50], ed3[50];
1011 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1012 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1013 const char *slot_api_empty_format="%c|%i||||||||\n";
1015 if (is_volume_name_legal(NULL, vol_name)) {
1016 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1017 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1018 memset(&pr, 0, sizeof(POOL_DBR));
1019 pr.PoolId = mr.PoolId;
1020 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1021 strcpy(pr.Name, "?");
1023 ua->send_msg(slot_api_full_format, type,
1024 Slot, mr.Slot, mr.VolumeName,
1025 edit_uint64(mr.VolBytes, ed1),
1026 mr.VolStatus, mr.MediaType, pr.Name,
1027 edit_uint64(mr.LastWritten, ed2),
1028 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1030 } else { /* Media unknown */
1031 ua->send_msg(slot_api_full_format,
1032 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1037 ua->send_msg(slot_api_empty_format, type, Slot);
1042 * Input (output of mxt-changer listall):
1044 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1045 * D:0:F:2:vol2 or D:Drive num:E
1050 * S:1:F:vol1 S:Slot num:F:Volume Name
1051 * S:2:E or S:Slot num:E
1054 * Import/Export tray slots:
1055 * I:10:F:vol10 I:Slot num:F:Volume Name
1056 * I:11:E or I:Slot num:E
1059 * If a drive is loaded, the slot *should* be empty
1063 * Drive list: D|Drive num|Slot loaded|Volume Name
1068 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1070 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1072 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1074 * TODO: need to merge with status_slots()
1076 void status_content(UAContext *ua, STORE *store)
1080 char dev_name[MAX_NAME_LENGTH];
1081 char vol_name[MAX_NAME_LENGTH];
1083 vol_list_t *vl=NULL, *vol_list = NULL;
1085 if (!(sd=open_sd_bsock(ua))) {
1089 if (!open_client_db(ua)) {
1093 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1094 bash_spaces(dev_name);
1095 /* Ask for autochanger list of volumes */
1096 sd->fsend(NT_("autochanger listall %s \n"), dev_name);
1098 /* Read and organize list of Drive, Slots and I/O Slots */
1099 while (sd->recv() >= 0) {
1100 strip_trailing_junk(sd->msg);
1102 /* Check for returned SD messages */
1103 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1104 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1105 sd->msg[4] == ' ') {
1106 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1113 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1114 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1116 /* we print information on the slot if we have a volume name */
1118 /* Add Slot and VolumeName to list */
1119 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1121 vl->VolName = bstrdup(vol_name);
1122 vl->next = vol_list;
1126 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1127 ua->send_msg("D|%d||\n", Drive);
1129 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1130 content_send_info(ua, type, Slot, vol_name);
1132 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1133 /* type can be S (slot) or I (Import/Export slot) */
1134 vol_list_t *prev=NULL;
1135 for (vl = vol_list; vl; vl = vl->next) {
1136 if (vl->Slot == Slot) {
1137 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1139 /* remove the node */
1141 prev->next = vl->next;
1143 vol_list = vl->next;
1151 content_send_info(ua, type, Slot, vol_name);
1154 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1161 * Print slots from AutoChanger
1163 void status_slots(UAContext *ua, STORE *store_r)
1167 vol_list_t *vl, *vol_list = NULL;
1172 /* Slot | Volume | Status | MediaType | Pool */
1173 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1176 status_content(ua, store_r);
1180 if (!open_client_db(ua)) {
1183 store.store = store_r;
1185 pm_strcpy(store.store_source, _("Command input"));
1186 set_wstorage(ua->jcr, &store);
1187 get_storage_drive(ua, store.store);
1189 max_slots = get_num_slots_from_SD(ua);
1191 if (max_slots <= 0) {
1192 ua->warning_msg(_("No slots in changer to scan.\n"));
1195 slot_list = (char *)malloc(max_slots+1);
1196 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1201 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1204 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1207 ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
1208 ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1210 /* Walk through the list getting the media records */
1211 for (vl=vol_list; vl; vl=vl->next) {
1212 if (vl->Slot > max_slots) {
1213 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1214 vl->Slot, max_slots);
1217 /* Check if user wants us to look at this slot */
1218 if (!slot_list[vl->Slot]) {
1219 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1223 slot_list[vl->Slot] = 0; /* clear Slot */
1226 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1227 ua->send_msg(slot_hformat,
1229 "?", "?", "?", "?");
1233 /* Hope that slots are ordered */
1234 for (; i < vl->Slot; i++) {
1236 ua->send_msg(slot_hformat,
1237 i, ' ', "", "", "", "");
1242 memset(&mr, 0, sizeof(MEDIA_DBR));
1243 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1245 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1246 memset(&pr, 0, sizeof(POOL_DBR));
1247 pr.PoolId = mr.PoolId;
1248 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1249 strcpy(pr.Name, "?");
1252 /* Print information */
1253 ua->send_msg(slot_hformat,
1254 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1255 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1257 } else { /* TODO: get information from catalog */
1258 ua->send_msg(slot_hformat,
1260 mr.VolumeName, "?", "?", "?");
1264 /* Display the rest of the autochanger
1266 for (; i <= max_slots; i++) {
1268 ua->send_msg(slot_hformat,
1269 i, ' ', "", "", "", "");
1276 free_vol_list(vol_list);