2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 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.
21 * Bacula Director -- Tape labeling commands
23 * Kern Sibbald, April MMIII
30 /* Slot list definition */
31 typedef struct s_vol_list {
32 struct s_vol_list *next;
38 /* Forward referenced functions */
39 static int do_label(UAContext *ua, const char *cmd, int relabel);
40 static void label_from_barcodes(UAContext *ua, int drive);
41 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
42 POOL_DBR *pr, int relabel, bool media_record_exits, int drive);
43 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan);
44 static void free_vol_list(vol_list_t *vol_list);
45 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr);
46 BSOCK *open_sd_bsock(UAContext *ua);
47 void close_sd_bsock(UAContext *ua);
48 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive);
49 static int get_num_slots_from_SD(UAContext *ua);
55 * label storage=xxx volume=vvv
57 int label_cmd(UAContext *ua, const char *cmd)
59 return do_label(ua, cmd, 0); /* standard label */
62 int relabel_cmd(UAContext *ua, const char *cmd)
64 return do_label(ua, cmd, 1); /* relabel tape */
67 static bool get_user_slot_list(UAContext *ua, char *slot_list, int num_slots)
72 /* slots are numbered 1 to num_slots */
73 for (int i=0; i <= num_slots; i++) {
76 i = find_arg_with_value(ua, "slots");
77 if (i == -1) { /* not found */
78 i = find_arg_with_value(ua, "slot");
81 /* scan slot list in ua->argv[i] */
85 strip_trailing_junk(ua->argv[i]);
86 for (p=ua->argv[i]; p && *p; p=e) {
93 h = strchr(p, '-'); /* range? */
95 msg = _("Negative numbers not permitted\n");
100 if (!is_an_integer(h)) {
101 msg = _("Range end is not integer.\n");
105 if (!is_an_integer(p)) {
106 msg = _("Range start is not an integer.\n");
112 msg = _("Range end not bigger than start.\n");
117 if (!is_an_integer(p)) {
118 msg = _("Input value is not an integer.\n");
123 if (beg <= 0 || end <= 0) {
124 msg = _("Values must be be greater than zero.\n");
127 if (end > num_slots) {
128 msg = _("Slot too large.\n");
131 for (i=beg; i<=end; i++) {
132 slot_list[i] = 1; /* Turn on specified range */
136 /* Turn everything on */
137 for (i=1; i <= num_slots; i++) {
141 if (debug_level >= 100) {
142 Dmsg0(100, "Slots turned on:\n");
143 for (i=1; i <= num_slots; i++) {
145 Dmsg1(100, "%d\n", i);
152 Dmsg1(100, "Problem with user selection ERR=%s\n", msg);
157 * Update Slots corresponding to Volumes in autochanger
159 void update_slots(UAContext *ua)
162 vol_list_t *vl, *vol_list = NULL;
173 if (!open_client_db(ua)) {
176 store.store = get_storage_resource(ua, true/*arg is storage*/);
180 pm_strcpy(store.store_source, _("command line"));
181 set_wstorage(ua->jcr, &store);
182 drive = get_storage_drive(ua, store.store);
184 scan = find_arg(ua, NT_("scan")) >= 0;
185 if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
186 Enabled = get_enabled(ua, ua->argv[i]);
192 have_enabled = false;
195 max_slots = get_num_slots_from_SD(ua);
196 Dmsg1(100, "max_slots=%d\n", max_slots);
197 if (max_slots <= 0) {
198 ua->warning_msg(_("No slots in changer to scan.\n"));
201 slot_list = (char *)malloc(max_slots+1);
202 if (!get_user_slot_list(ua, slot_list, max_slots)) {
207 vol_list = get_vol_list_from_SD(ua, scan);
210 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
214 /* First zap out any InChanger with StorageId=0 */
215 db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);
217 /* Walk through the list updating the media records */
218 for (vl=vol_list; vl; vl=vl->next) {
219 if (vl->Slot > max_slots) {
220 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
221 vl->Slot, max_slots);
224 /* Check if user wants us to look at this slot */
225 if (!slot_list[vl->Slot]) {
226 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
229 /* If scanning, we read the label rather than the barcode */
235 vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
236 Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
238 slot_list[vl->Slot] = 0; /* clear Slot */
241 mr.MediaId = 0; /* Force using VolumeName */
243 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
245 mr.VolumeName[0] = 0;
247 set_storageid_in_mr(store.store, &mr);
248 Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
249 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
251 /* Set InChanger to zero for this Slot */
252 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
254 Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
255 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
257 Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
258 ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
262 Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
263 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
264 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
265 Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
266 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
267 /* If Slot, Inchanger, and StorageId have changed, update the Media record */
268 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
272 mr.Enabled = Enabled;
274 set_storageid_in_mr(store.store, &mr);
275 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
276 ua->error_msg("%s", db_strerror(ua->db));
279 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
280 mr.VolumeName, mr.Slot);
283 ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
289 ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
290 mr.VolumeName, vl->Slot);
296 set_storageid_in_mr(store.store, &mr);
298 for (int i=1; i <= max_slots; i++) {
301 /* Set InChanger to zero for this Slot */
302 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
309 free_vol_list(vol_list);
319 * Common routine for both label and relabel
321 static int do_label(UAContext *ua, const char *cmd, int relabel)
325 char dev_name[MAX_NAME_LENGTH];
328 bool print_reminder = true;
329 bool label_barcodes = false;
333 bool media_record_exists = false;
334 static const char *barcode_keyword[] = {
340 memset(&pr, 0, sizeof(pr));
341 if (!open_client_db(ua)) {
345 /* Look for one of the barcode keywords */
346 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
347 /* Now find the keyword in the list */
348 if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
349 *ua->argk[j] = 0; /* zap barcode keyword */
351 label_barcodes = true;
354 store.store = get_storage_resource(ua, true/*use default*/);
358 pm_strcpy(store.store_source, _("command line"));
359 set_wstorage(ua->jcr, &store);
360 drive = get_storage_drive(ua, store.store);
362 if (label_barcodes) {
363 label_from_barcodes(ua, drive);
367 /* If relabel get name of Volume to relabel */
369 /* Check for oldvolume=name */
370 i = find_arg_with_value(ua, "oldvolume");
372 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
374 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
377 ua->error_msg("%s", db_strerror(ua->db));
379 /* No keyword or Vol not found, ask user to select */
380 if (!select_media_dbr(ua, &omr)) {
384 /* Require Volume to be Purged or Recycled */
386 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
387 ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
388 omr.VolumeName, omr.VolStatus);
393 /* Check for volume=NewVolume */
394 i = find_arg_with_value(ua, "volume");
396 pm_strcpy(ua->cmd, ua->argv[i]);
400 /* Get a new Volume name */
402 media_record_exists = false;
403 if (!get_cmd(ua, _("Enter new Volume name: "))) {
407 if (!is_volume_name_legal(ua, ua->cmd)) {
411 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
413 /* If VolBytes are zero the Volume is not labeled */
414 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
415 if (mr.VolBytes != 0) {
416 ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
420 media_record_exists = true;
425 /* If autochanger, request slot */
426 i = find_arg_with_value(ua, "slot");
428 mr.Slot = atoi(ua->argv[i]);
432 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
433 } else if (store.store->autochanger) {
434 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
437 mr.Slot = ua->pint32_val;
441 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
443 set_storageid_in_mr(store.store, &mr);
445 bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
447 /* Must select Pool if not already done */
448 if (pr.PoolId == 0) {
449 memset(&pr, 0, sizeof(pr));
450 if (!select_pool_dbr(ua, &pr)) {
455 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
458 sd = ua->jcr->store_bsock;
460 /* Delete the old media record */
461 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
462 ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
463 omr.VolumeName, db_strerror(ua->db));
465 ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"),
467 /* Update the number of Volumes in the pool */
469 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
470 ua->error_msg("%s", db_strerror(ua->db));
475 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
476 ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
477 bash_spaces(dev_name);
478 sd->fsend("mount %s drive=%d slot=%d", dev_name, drive, mr.Slot);
479 unbash_spaces(dev_name);
480 while (sd->recv() >= 0) {
481 ua->send_msg("%s", sd->msg);
483 * 3001 OK mount. Device=xxx or
484 * 3001 Mounted Volume vvvv
485 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
486 * 3906 is cannot mount non-tape
487 * So for those, no need to print a reminder
489 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
490 strncmp(sd->msg, "3002 ", 5) == 0 ||
491 strncmp(sd->msg, "3906 ", 5) == 0) {
492 print_reminder = false;
497 if (print_reminder) {
498 ua->info_msg(_("Do not forget to mount the drive!!!\n"));
506 * Request SD to send us the slot:barcodes, then wiffle
507 * through them all labeling them.
509 static void label_from_barcodes(UAContext *ua, int drive)
511 STORE *store = ua->jcr->wstore;
514 vol_list_t *vl, *vol_list = NULL;
515 bool media_record_exists;
520 max_slots = get_num_slots_from_SD(ua);
521 if (max_slots <= 0) {
522 ua->warning_msg(_("No slots in changer to scan.\n"));
525 slot_list = (char *)malloc(max_slots+1);
526 if (!get_user_slot_list(ua, slot_list, max_slots)) {
530 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
533 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
537 /* Display list of Volumes and ask if he really wants to proceed */
538 ua->send_msg(_("The following Volumes will be labeled:\n"
540 "==============\n"));
541 for (vl=vol_list; vl; vl=vl->next) {
542 if (!vl->VolName || !slot_list[vl->Slot]) {
545 ua->send_msg("%4d %s\n", vl->Slot, vl->VolName);
547 if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
548 (ua->pint32_val == 0)) {
552 memset(&pr, 0, sizeof(pr));
553 if (!select_pool_dbr(ua, &pr)) {
557 /* Fire off the label requests */
558 for (vl=vol_list; vl; vl=vl->next) {
559 if (!vl->VolName || !slot_list[vl->Slot]) {
563 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
564 media_record_exists = false;
565 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
566 if (mr.VolBytes != 0) {
567 ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
568 vl->Slot, mr.VolumeName);
570 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
571 set_storageid_in_mr(store, &mr);
572 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
573 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
577 media_record_exists = true;
579 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
580 set_storageid_in_mr(store, &mr);
582 * Deal with creating cleaning tape here. Normal tapes created in
583 * send_label_request() below
585 if (is_cleaning_tape(ua, &mr, &pr)) {
586 if (media_record_exists) { /* we update it */
587 mr.VolBytes = 1; /* any bytes to indicate it exists */
588 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
590 set_storageid_in_mr(store, &mr);
591 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
592 ua->error_msg("%s", db_strerror(ua->db));
594 } else { /* create the media record */
595 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
596 ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
599 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
600 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
602 set_storageid_in_mr(store, &mr);
603 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
604 ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
606 pr.NumVols++; /* this is a bit suspect */
607 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
608 ua->error_msg("%s", db_strerror(ua->db));
611 ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
614 continue; /* done, go handle next volume */
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 (sd->recv() >= 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) {
715 unbash_spaces(mr->VolumeName);
716 unbash_spaces(mr->MediaType);
717 unbash_spaces(pr->Name);
718 mr->LabelDate = time(NULL);
719 mr->set_label_date = true;
721 if (media_record_exists) { /* we update it */
722 mr->VolBytes = VolBytes;
723 mr->VolABytes = VolABytes;
724 mr->VolType = VolType;
725 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
726 set_storageid_in_mr(ua->jcr->wstore, mr);
727 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
728 ua->error_msg("%s", db_strerror(ua->db));
731 } else { /* create the media record */
732 set_pool_dbr_defaults_in_media_dbr(mr, pr);
733 mr->VolBytes = VolBytes;
734 mr->VolABytes = VolABytes;
735 mr->VolType = VolType;
736 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
738 set_storageid_in_mr(ua->jcr->wstore, mr);
739 if (db_create_media_record(ua->jcr, ua->db, mr)) {
740 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
741 mr->VolumeName, mr->Slot);
742 /* Update number of volumes in pool */
744 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
745 ua->error_msg("%s", db_strerror(ua->db));
748 ua->error_msg("%s", db_strerror(ua->db));
753 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
758 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
760 STORE *store = ua->jcr->wstore;
762 char dev_name[MAX_NAME_LENGTH];
763 char *VolName = NULL;
766 if (!(sd=open_sd_bsock(ua))) {
767 ua->error_msg(_("Could not open SD socket.\n"));
770 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
771 bash_spaces(dev_name);
772 /* Ask for autochanger list of volumes */
773 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
774 Dmsg1(100, "Sent: %s", sd->msg);
776 /* Get Volume name in this Slot */
777 while (sd->recv() >= 0) {
778 ua->send_msg("%s", sd->msg);
779 Dmsg1(100, "Got: %s", sd->msg);
780 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
781 VolName = (char *)malloc(sd->msglen);
782 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
790 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
795 * We get the slot list from the Storage daemon.
796 * If scan is set, we return all slots found,
797 * otherwise, we return only slots with valid barcodes (Volume names)
799 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
801 STORE *store = ua->jcr->wstore;
802 char dev_name[MAX_NAME_LENGTH];
805 vol_list_t *vol_list = NULL;
808 if (!(sd=open_sd_bsock(ua))) {
812 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
813 bash_spaces(dev_name);
814 /* Ask for autochanger list of volumes */
815 sd->fsend(NT_("autochanger list %s \n"), dev_name);
817 /* Read and organize list of Volumes */
818 while (sd->recv() >= 0) {
821 strip_trailing_junk(sd->msg);
823 /* Check for returned SD messages */
824 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
825 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
827 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
831 /* Validate Slot: if scanning, otherwise Slot:Barcode */
832 p = strchr(sd->msg, ':');
834 /* Scanning -- require only valid slot */
835 Slot = atoi(sd->msg);
839 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
844 if (p && strlen(p) > 1) {
846 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
849 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
855 if (!is_volume_name_legal(ua, p)) {
858 ua->error_msg(_("Invalid Volume name: %s. Volume skipped.\n"), sd->msg);
863 /* Add Slot and VolumeName to list */
864 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
868 p++; /* skip separator */
870 vl->VolName = bstrdup(p);
874 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
879 vol_list_t *prev=vol_list;
880 /* Add new entry to the right place in the list */
881 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
882 if (tvl->Slot > vl->Slot) {
883 /* no previous item, update vol_list directly */
884 if (prev == vol_list) {
888 } else { /* replace the previous pointer */
894 /* we are at the end */
908 static void free_vol_list(vol_list_t *vol_list)
913 for (vl=vol_list; vl; ) {
925 * We get the number of slots in the changer from the SD
927 static int get_num_slots_from_SD(UAContext *ua)
929 STORE *store = ua->jcr->wstore;
930 char dev_name[MAX_NAME_LENGTH];
935 if (!(sd=open_sd_bsock(ua))) {
939 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
940 bash_spaces(dev_name);
941 /* Ask for autochanger number of slots */
942 sd->fsend(NT_("autochanger slots %s\n"), dev_name);
944 while (sd->recv() >= 0) {
945 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
948 ua->send_msg("%s", sd->msg);
952 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
957 * We get the number of drives in the changer from the SD
959 int get_num_drives_from_SD(UAContext *ua)
961 STORE *store = ua->jcr->wstore;
962 char dev_name[MAX_NAME_LENGTH];
967 if (!(sd=open_sd_bsock(ua))) {
971 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
972 bash_spaces(dev_name);
973 /* Ask for autochanger number of slots */
974 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
976 while (sd->recv() >= 0) {
977 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
980 ua->send_msg("%s", sd->msg);
984 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
989 * Check if this is a cleaning tape by comparing the Volume name
990 * with the Cleaning Prefix. If they match, this is a cleaning
993 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
995 /* Find Pool resource */
996 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
997 if (!ua->jcr->pool) {
998 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
999 pr->Name, mr->VolumeName);
1002 if (ua->jcr->pool->cleaning_prefix == NULL) {
1005 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1006 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1007 strlen(ua->jcr->pool->cleaning_prefix),
1008 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1009 strlen(ua->jcr->pool->cleaning_prefix)));
1010 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1011 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1014 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1016 char ed1[50], ed2[50], ed3[50];
1019 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1020 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1021 const char *slot_api_empty_format="%c|%i||||||||\n";
1023 if (is_volume_name_legal(NULL, vol_name)) {
1024 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1025 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1026 memset(&pr, 0, sizeof(POOL_DBR));
1027 pr.PoolId = mr.PoolId;
1028 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1029 strcpy(pr.Name, "?");
1031 ua->send_msg(slot_api_full_format, type,
1032 Slot, mr.Slot, mr.VolumeName,
1033 edit_uint64(mr.VolBytes, ed1),
1034 mr.VolStatus, mr.MediaType, pr.Name,
1035 edit_uint64(mr.LastWritten, ed2),
1036 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1038 } else { /* Media unknown */
1039 ua->send_msg(slot_api_full_format,
1040 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1045 ua->send_msg(slot_api_empty_format, type, Slot);
1050 * Input (output of mxt-changer listall):
1052 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1053 * D:0:F:2:vol2 or D:Drive num:E
1058 * S:1:F:vol1 S:Slot num:F:Volume Name
1059 * S:2:E or S:Slot num:E
1062 * Import/Export tray slots:
1063 * I:10:F:vol10 I:Slot num:F:Volume Name
1064 * I:11:E or I:Slot num:E
1067 * If a drive is loaded, the slot *should* be empty
1071 * Drive list: D|Drive num|Slot loaded|Volume Name
1076 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1078 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1080 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1082 * TODO: need to merge with status_slots()
1084 void status_content(UAContext *ua, STORE *store)
1088 char dev_name[MAX_NAME_LENGTH];
1089 char vol_name[MAX_NAME_LENGTH];
1091 vol_list_t *vl=NULL, *vol_list = NULL;
1093 if (!(sd=open_sd_bsock(ua))) {
1097 if (!open_client_db(ua)) {
1101 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1102 bash_spaces(dev_name);
1103 /* Ask for autochanger list of volumes */
1104 sd->fsend(NT_("autochanger listall %s \n"), dev_name);
1106 /* Read and organize list of Drive, Slots and I/O Slots */
1107 while (sd->recv() >= 0) {
1108 strip_trailing_junk(sd->msg);
1110 /* Check for returned SD messages */
1111 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1112 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1113 sd->msg[4] == ' ') {
1114 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1121 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1122 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1124 /* we print information on the slot if we have a volume name */
1126 /* Add Slot and VolumeName to list */
1127 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1129 vl->VolName = bstrdup(vol_name);
1130 vl->next = vol_list;
1134 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1135 ua->send_msg("D|%d||\n", Drive);
1137 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1138 content_send_info(ua, type, Slot, vol_name);
1140 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1141 /* type can be S (slot) or I (Import/Export slot) */
1142 vol_list_t *prev=NULL;
1143 for (vl = vol_list; vl; vl = vl->next) {
1144 if (vl->Slot == Slot) {
1145 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1147 /* remove the node */
1149 prev->next = vl->next;
1151 vol_list = vl->next;
1159 content_send_info(ua, type, Slot, vol_name);
1162 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1169 * Print slots from AutoChanger
1171 void status_slots(UAContext *ua, STORE *store_r)
1175 vol_list_t *vl, *vol_list = NULL;
1180 /* Slot | Volume | Status | MediaType | Pool */
1181 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1184 status_content(ua, store_r);
1188 if (!open_client_db(ua)) {
1191 store.store = store_r;
1193 pm_strcpy(store.store_source, _("command line"));
1194 set_wstorage(ua->jcr, &store);
1195 get_storage_drive(ua, store.store);
1197 max_slots = get_num_slots_from_SD(ua);
1199 if (max_slots <= 0) {
1200 ua->warning_msg(_("No slots in changer to scan.\n"));
1203 slot_list = (char *)malloc(max_slots+1);
1204 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1209 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1212 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1215 ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
1216 ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1218 /* Walk through the list getting the media records */
1219 for (vl=vol_list; vl; vl=vl->next) {
1220 if (vl->Slot > max_slots) {
1221 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1222 vl->Slot, max_slots);
1225 /* Check if user wants us to look at this slot */
1226 if (!slot_list[vl->Slot]) {
1227 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1231 slot_list[vl->Slot] = 0; /* clear Slot */
1234 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1235 ua->send_msg(slot_hformat,
1237 "?", "?", "?", "?");
1241 /* Hope that slots are ordered */
1242 for (; i < vl->Slot; i++) {
1244 ua->send_msg(slot_hformat,
1245 i, ' ', "", "", "", "");
1250 memset(&mr, 0, sizeof(MEDIA_DBR));
1251 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1253 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1254 memset(&pr, 0, sizeof(POOL_DBR));
1255 pr.PoolId = mr.PoolId;
1256 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1257 strcpy(pr.Name, "?");
1260 /* Print information */
1261 ua->send_msg(slot_hformat,
1262 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1263 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1265 } else { /* TODO: get information from catalog */
1266 ua->send_msg(slot_hformat,
1268 mr.VolumeName, "?", "?", "?");
1272 /* Display the rest of the autochanger
1274 for (; i <= max_slots; i++) {
1276 ua->send_msg(slot_hformat,
1277 i, ' ', "", "", "", "");
1284 free_vol_list(vol_list);