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 */
615 /* Not a cleaning tape */
616 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
619 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
625 free_vol_list(vol_list);
632 * Check if the Volume name has legal characters
633 * If ua is non-NULL send the message
635 bool is_volume_name_legal(UAContext *ua, const char *name)
639 const char *accept = ":.-_";
641 /* Restrict the characters permitted in the Volume name */
642 for (p=name; *p; p++) {
643 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
647 ua->error_msg(_("Illegal character \"%c\" in a volume name.\n"), *p);
652 if (len >= MAX_NAME_LENGTH) {
654 ua->error_msg(_("Volume name too long.\n"));
660 ua->error_msg(_("Volume name must be at least one character long.\n"));
668 * NOTE! This routine opens the SD socket but leaves it open
670 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
671 POOL_DBR *pr, int relabel, bool media_record_exists,
675 char dev_name[MAX_NAME_LENGTH];
677 uint64_t VolBytes = 0;
678 uint64_t VolABytes = 0;
679 uint32_t VolType = 0;
681 if (!(sd=open_sd_bsock(ua))) {
684 bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
685 bash_spaces(dev_name);
686 bash_spaces(mr->VolumeName);
687 bash_spaces(mr->MediaType);
688 bash_spaces(pr->Name);
690 bash_spaces(omr->VolumeName);
691 sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
692 "MediaType=%s Slot=%d drive=%d",
693 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
694 mr->MediaType, mr->Slot, drive);
695 ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
696 omr->VolumeName, mr->VolumeName);
698 sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
700 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
702 ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"),
703 mr->VolumeName, mr->Slot);
704 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
705 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
708 while (bget_dirmsg(sd) >= 0) {
709 ua->send_msg("%s", sd->msg);
710 if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu VolABytes=%lld VolType=%d ",
711 &VolBytes, &VolABytes, &VolType) == 3) {
713 if (media_record_exists) { /* we update it */
714 mr->VolBytes = VolBytes;
715 mr->VolABytes = VolABytes;
716 mr->VolType = VolType;
717 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
718 set_storageid_in_mr(ua->jcr->wstore, mr);
719 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
720 ua->error_msg("%s", db_strerror(ua->db));
723 } else { /* create the media record */
724 set_pool_dbr_defaults_in_media_dbr(mr, pr);
725 mr->VolBytes = VolBytes;
726 mr->VolABytes = VolABytes;
727 mr->VolType = VolType;
728 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
730 set_storageid_in_mr(ua->jcr->wstore, mr);
731 if (db_create_media_record(ua->jcr, ua->db, mr)) {
732 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
733 mr->VolumeName, mr->Slot);
734 /* Update number of volumes in pool */
736 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
737 ua->error_msg("%s", db_strerror(ua->db));
740 ua->error_msg("%s", db_strerror(ua->db));
747 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
752 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
754 STORE *store = ua->jcr->wstore;
756 char dev_name[MAX_NAME_LENGTH];
757 char *VolName = NULL;
760 if (!(sd=open_sd_bsock(ua))) {
761 ua->error_msg(_("Could not open SD socket.\n"));
764 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
765 bash_spaces(dev_name);
766 /* Ask for autochanger list of volumes */
767 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
768 Dmsg1(100, "Sent: %s", sd->msg);
770 /* Get Volume name in this Slot */
771 while (sd->recv() >= 0) {
772 ua->send_msg("%s", sd->msg);
773 Dmsg1(100, "Got: %s", sd->msg);
774 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
775 VolName = (char *)malloc(sd->msglen);
776 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
784 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
789 * We get the slot list from the Storage daemon.
790 * If scan is set, we return all slots found,
791 * otherwise, we return only slots with valid barcodes (Volume names)
793 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
795 STORE *store = ua->jcr->wstore;
796 char dev_name[MAX_NAME_LENGTH];
799 vol_list_t *vol_list = NULL;
802 if (!(sd=open_sd_bsock(ua))) {
806 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
807 bash_spaces(dev_name);
808 /* Ask for autochanger list of volumes */
809 sd->fsend(NT_("autochanger list %s \n"), dev_name);
811 /* Read and organize list of Volumes */
812 while (sd->recv() >= 0) {
815 strip_trailing_junk(sd->msg);
817 /* Check for returned SD messages */
818 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
819 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
821 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
825 /* Validate Slot: if scanning, otherwise Slot:Barcode */
826 p = strchr(sd->msg, ':');
828 /* Scanning -- require only valid slot */
829 Slot = atoi(sd->msg);
833 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
838 if (p && strlen(p) > 1) {
840 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
843 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
849 if (!is_volume_name_legal(ua, p)) {
852 ua->error_msg(_("Invalid Volume name: %s. Volume skipped.\n"), sd->msg);
857 /* Add Slot and VolumeName to list */
858 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
862 p++; /* skip separator */
864 vl->VolName = bstrdup(p);
868 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
873 vol_list_t *prev=vol_list;
874 /* Add new entry to the right place in the list */
875 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
876 if (tvl->Slot > vl->Slot) {
877 /* no previous item, update vol_list directly */
878 if (prev == vol_list) {
882 } else { /* replace the previous pointer */
888 /* we are at the end */
902 static void free_vol_list(vol_list_t *vol_list)
907 for (vl=vol_list; vl; ) {
919 * We get the number of slots in the changer from the SD
921 static int get_num_slots_from_SD(UAContext *ua)
923 STORE *store = ua->jcr->wstore;
924 char dev_name[MAX_NAME_LENGTH];
929 if (!(sd=open_sd_bsock(ua))) {
933 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
934 bash_spaces(dev_name);
935 /* Ask for autochanger number of slots */
936 sd->fsend(NT_("autochanger slots %s\n"), dev_name);
938 while (sd->recv() >= 0) {
939 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
942 ua->send_msg("%s", sd->msg);
946 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
951 * We get the number of drives in the changer from the SD
953 int get_num_drives_from_SD(UAContext *ua)
955 STORE *store = ua->jcr->wstore;
956 char dev_name[MAX_NAME_LENGTH];
961 if (!(sd=open_sd_bsock(ua))) {
965 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
966 bash_spaces(dev_name);
967 /* Ask for autochanger number of slots */
968 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
970 while (sd->recv() >= 0) {
971 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
974 ua->send_msg("%s", sd->msg);
978 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
983 * Check if this is a cleaning tape by comparing the Volume name
984 * with the Cleaning Prefix. If they match, this is a cleaning
987 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
989 if (ua->jcr->pool->cleaning_prefix == NULL) {
990 return false; /* if no cleaning prefix, this is not a cleaning tape */
992 /* Find Pool resource */
993 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
994 if (!ua->jcr->pool) {
995 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
996 pr->Name, mr->VolumeName);
999 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1000 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1001 strlen(ua->jcr->pool->cleaning_prefix),
1002 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1003 strlen(ua->jcr->pool->cleaning_prefix)));
1004 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1005 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1009 * Send Volume info to caller in API format
1011 static void send_volume_info(UAContext *ua, char type, int Slot, char *vol_name)
1013 char ed1[50], ed2[50], ed3[50];
1016 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1017 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1018 const char *slot_api_empty_format="%c|%i||||||||\n";
1020 if (is_volume_name_legal(NULL, vol_name)) {
1021 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1022 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1023 memset(&pr, 0, sizeof(POOL_DBR));
1024 pr.PoolId = mr.PoolId;
1025 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1026 strcpy(pr.Name, "?");
1028 ua->send_msg(slot_api_full_format, type,
1029 Slot, mr.Slot, mr.VolumeName,
1030 edit_uint64(mr.VolBytes, ed1),
1031 mr.VolStatus, mr.MediaType, pr.Name,
1032 edit_uint64(mr.LastWritten, ed2),
1033 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1035 } else { /* Media unknown */
1036 ua->send_msg(slot_api_full_format,
1037 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1042 ua->send_msg(slot_api_empty_format, type, Slot);
1047 * Input (output of mxt-changer listall):
1049 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1050 * D:0:F:2:vol2 or D:Drive num:E
1055 * S:1:F:vol1 S:Slot num:F:Volume Name
1056 * S:2:E or S:Slot num:E
1059 * Import/Export tray slots:
1060 * I:10:F:vol10 I:Slot num:F:Volume Name
1061 * I:11:E or I:Slot num:E
1064 * If a drive is loaded, the slot *should* be empty
1068 * Drive list: D|Drive num|Slot loaded|Volume Name
1073 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1075 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1077 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1079 * TODO: need to merge with status_slots()
1081 void status_content(UAContext *ua, STORE *store)
1085 char dev_name[MAX_NAME_LENGTH];
1086 char vol_name[MAX_NAME_LENGTH];
1088 vol_list_t *vl=NULL, *vol_list = NULL;
1090 if (!(sd=open_sd_bsock(ua))) {
1094 if (!open_client_db(ua)) {
1098 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1099 bash_spaces(dev_name);
1100 /* Ask for autochanger list of volumes */
1101 sd->fsend(NT_("autochanger listall %s \n"), dev_name);
1103 /* Read and organize list of Drive, Slots and I/O Slots */
1104 while (sd->recv() >= 0) {
1105 strip_trailing_junk(sd->msg);
1107 /* Check for returned SD messages */
1108 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1109 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1110 sd->msg[4] == ' ') {
1111 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1118 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1119 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1121 /* we print information on the slot if we have a volume name */
1123 /* Add Slot and VolumeName to list */
1124 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1126 vl->VolName = bstrdup(vol_name);
1127 vl->next = vol_list;
1131 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1132 ua->send_msg("D|%d||\n", Drive);
1134 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1135 send_volume_info(ua, type, Slot, vol_name);
1137 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1138 /* type can be S (slot) or I (Import/Export slot) */
1139 vol_list_t *prev=NULL;
1140 for (vl = vol_list; vl; vl = vl->next) {
1141 if (vl->Slot == Slot) {
1142 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1144 /* remove the node */
1146 prev->next = vl->next;
1148 vol_list = vl->next;
1156 send_volume_info(ua, type, Slot, vol_name);
1159 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1166 * Print slots from AutoChanger
1168 void status_slots(UAContext *ua, STORE *store_r)
1172 vol_list_t *vl, *vol_list = NULL;
1177 /* Slot | Volume | Status | MediaType | Pool */
1178 const char *slot_hformat="| %4i%c| %-20s | %-9s | %-15s | %-18s |\n";
1181 status_content(ua, store_r);
1185 if (!open_client_db(ua)) {
1188 store.store = store_r;
1190 pm_strcpy(store.store_source, _("Command input"));
1191 set_wstorage(ua->jcr, &store);
1192 get_storage_drive(ua, store.store);
1194 max_slots = get_num_slots_from_SD(ua);
1196 if (max_slots <= 0) {
1197 ua->warning_msg(_("No slots in changer to scan.\n"));
1200 slot_list = (char *)malloc(max_slots+1);
1201 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1206 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1209 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1212 ua->send_msg(_("+------+----------------------+-----------+-----------------+--------------------+\n"));
1213 ua->send_msg(_("| Slot | Volume Name | Status | Media Type | Pool |\n"));
1214 ua->send_msg(_("+------+----------------------+-----------+-----------------+--------------------+\n"));
1216 /* Walk through the list getting the media records */
1217 for (vl=vol_list; vl; vl=vl->next) {
1218 if (vl->Slot > max_slots) {
1219 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1220 vl->Slot, max_slots);
1223 /* Check if user wants us to look at this slot */
1224 if (!slot_list[vl->Slot]) {
1225 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1229 slot_list[vl->Slot] = 0; /* clear Slot */
1232 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1233 ua->send_msg(slot_hformat,
1235 "?", "?", "?", "?");
1239 /* Hope that slots are ordered */
1240 for (; i < vl->Slot; i++) {
1242 ua->send_msg(slot_hformat,
1243 i, ' ', "", "", "", "");
1247 ua->send_msg(_("+------+----------------------+-----------+-----------------+--------------------+\n"));
1249 memset(&mr, 0, sizeof(MEDIA_DBR));
1250 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1252 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1253 memset(&pr, 0, sizeof(POOL_DBR));
1254 pr.PoolId = mr.PoolId;
1255 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1256 strcpy(pr.Name, "?");
1259 /* Print information */
1260 ua->send_msg(slot_hformat,
1261 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1262 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1264 } else { /* TODO: get information from catalog */
1265 ua->send_msg(slot_hformat,
1267 mr.VolumeName, "?", "?", "?");
1271 /* Display the rest of the autochanger
1273 for (; i <= max_slots; i++) {
1275 ua->send_msg(slot_hformat,
1276 i, ' ', "", "", "", "");
1283 free_vol_list(vol_list);