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 (strstr((const POOLMEM *) sd->msg, "3000 OK label")) {
712 if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu VolABytes=%lld VolType=%d ",
713 &VolBytes, &VolABytes, &VolType) == 3) {
718 ua->error_msg("Volume label failed: Storage Daemon and Director are incompatible versions.");
721 unbash_spaces(mr->VolumeName);
722 unbash_spaces(mr->MediaType);
723 unbash_spaces(pr->Name);
724 mr->LabelDate = time(NULL);
725 mr->set_label_date = true;
727 if (media_record_exists) { /* we update it */
728 mr->VolBytes = VolBytes;
729 mr->VolABytes = VolABytes;
730 mr->VolType = VolType;
731 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
732 set_storageid_in_mr(ua->jcr->wstore, mr);
733 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
734 ua->error_msg("%s", db_strerror(ua->db));
737 } else { /* create the media record */
738 set_pool_dbr_defaults_in_media_dbr(mr, pr);
739 mr->VolBytes = VolBytes;
740 mr->VolABytes = VolABytes;
741 mr->VolType = VolType;
742 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
744 set_storageid_in_mr(ua->jcr->wstore, mr);
745 if (db_create_media_record(ua->jcr, ua->db, mr)) {
746 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
747 mr->VolumeName, mr->Slot);
748 /* Update number of volumes in pool */
750 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
751 ua->error_msg("%s", db_strerror(ua->db));
754 ua->error_msg("%s", db_strerror(ua->db));
759 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
764 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
766 STORE *store = ua->jcr->wstore;
768 char dev_name[MAX_NAME_LENGTH];
769 char *VolName = NULL;
772 if (!(sd=open_sd_bsock(ua))) {
773 ua->error_msg(_("Could not open SD socket.\n"));
776 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
777 bash_spaces(dev_name);
778 /* Ask for autochanger list of volumes */
779 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
780 Dmsg1(100, "Sent: %s", sd->msg);
782 /* Get Volume name in this Slot */
783 while (sd->recv() >= 0) {
784 ua->send_msg("%s", sd->msg);
785 Dmsg1(100, "Got: %s", sd->msg);
786 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
787 VolName = (char *)malloc(sd->msglen);
788 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
796 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
801 * We get the slot list from the Storage daemon.
802 * If scan is set, we return all slots found,
803 * otherwise, we return only slots with valid barcodes (Volume names)
805 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
807 STORE *store = ua->jcr->wstore;
808 char dev_name[MAX_NAME_LENGTH];
811 vol_list_t *vol_list = NULL;
814 if (!(sd=open_sd_bsock(ua))) {
818 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
819 bash_spaces(dev_name);
820 /* Ask for autochanger list of volumes */
821 sd->fsend(NT_("autochanger list %s \n"), dev_name);
823 /* Read and organize list of Volumes */
824 while (sd->recv() >= 0) {
827 strip_trailing_junk(sd->msg);
829 /* Check for returned SD messages */
830 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
831 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
833 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
837 /* Validate Slot: if scanning, otherwise Slot:Barcode */
838 p = strchr(sd->msg, ':');
840 /* Scanning -- require only valid slot */
841 Slot = atoi(sd->msg);
845 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
850 if (p && strlen(p) > 1) {
852 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
855 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
861 if (!is_volume_name_legal(ua, p)) {
864 ua->error_msg(_("Invalid Volume name: %s. Volume skipped.\n"), sd->msg);
869 /* Add Slot and VolumeName to list */
870 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
874 p++; /* skip separator */
876 vl->VolName = bstrdup(p);
880 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
885 vol_list_t *prev=vol_list;
886 /* Add new entry to the right place in the list */
887 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
888 if (tvl->Slot > vl->Slot) {
889 /* no previous item, update vol_list directly */
890 if (prev == vol_list) {
894 } else { /* replace the previous pointer */
900 /* we are at the end */
914 static void free_vol_list(vol_list_t *vol_list)
919 for (vl=vol_list; vl; ) {
931 * We get the number of slots in the changer from the SD
933 static int get_num_slots_from_SD(UAContext *ua)
935 STORE *store = ua->jcr->wstore;
936 char dev_name[MAX_NAME_LENGTH];
941 if (!(sd=open_sd_bsock(ua))) {
945 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
946 bash_spaces(dev_name);
947 /* Ask for autochanger number of slots */
948 sd->fsend(NT_("autochanger slots %s\n"), dev_name);
950 while (sd->recv() >= 0) {
951 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
954 ua->send_msg("%s", sd->msg);
958 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
963 * We get the number of drives in the changer from the SD
965 int get_num_drives_from_SD(UAContext *ua)
967 STORE *store = ua->jcr->wstore;
968 char dev_name[MAX_NAME_LENGTH];
973 if (!(sd=open_sd_bsock(ua))) {
977 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
978 bash_spaces(dev_name);
979 /* Ask for autochanger number of slots */
980 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
982 while (sd->recv() >= 0) {
983 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
986 ua->send_msg("%s", sd->msg);
990 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
995 * Check if this is a cleaning tape by comparing the Volume name
996 * with the Cleaning Prefix. If they match, this is a cleaning
999 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
1001 /* Find Pool resource */
1002 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
1003 if (!ua->jcr->pool) {
1004 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
1005 pr->Name, mr->VolumeName);
1008 if (ua->jcr->pool->cleaning_prefix == NULL) {
1011 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1012 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1013 strlen(ua->jcr->pool->cleaning_prefix),
1014 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1015 strlen(ua->jcr->pool->cleaning_prefix)));
1016 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1017 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1020 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1022 char ed1[50], ed2[50], ed3[50];
1025 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1026 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1027 const char *slot_api_empty_format="%c|%i||||||||\n";
1029 if (is_volume_name_legal(NULL, vol_name)) {
1030 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1031 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1032 memset(&pr, 0, sizeof(POOL_DBR));
1033 pr.PoolId = mr.PoolId;
1034 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1035 strcpy(pr.Name, "?");
1037 ua->send_msg(slot_api_full_format, type,
1038 Slot, mr.Slot, mr.VolumeName,
1039 edit_uint64(mr.VolBytes, ed1),
1040 mr.VolStatus, mr.MediaType, pr.Name,
1041 edit_uint64(mr.LastWritten, ed2),
1042 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1044 } else { /* Media unknown */
1045 ua->send_msg(slot_api_full_format,
1046 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1051 ua->send_msg(slot_api_empty_format, type, Slot);
1056 * Input (output of mxt-changer listall):
1058 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1059 * D:0:F:2:vol2 or D:Drive num:E
1064 * S:1:F:vol1 S:Slot num:F:Volume Name
1065 * S:2:E or S:Slot num:E
1068 * Import/Export tray slots:
1069 * I:10:F:vol10 I:Slot num:F:Volume Name
1070 * I:11:E or I:Slot num:E
1073 * If a drive is loaded, the slot *should* be empty
1077 * Drive list: D|Drive num|Slot loaded|Volume Name
1082 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1084 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1086 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1088 * TODO: need to merge with status_slots()
1090 void status_content(UAContext *ua, STORE *store)
1094 char dev_name[MAX_NAME_LENGTH];
1095 char vol_name[MAX_NAME_LENGTH];
1097 vol_list_t *vl=NULL, *vol_list = NULL;
1099 if (!(sd=open_sd_bsock(ua))) {
1103 if (!open_client_db(ua)) {
1107 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1108 bash_spaces(dev_name);
1109 /* Ask for autochanger list of volumes */
1110 sd->fsend(NT_("autochanger listall %s \n"), dev_name);
1112 /* Read and organize list of Drive, Slots and I/O Slots */
1113 while (sd->recv() >= 0) {
1114 strip_trailing_junk(sd->msg);
1116 /* Check for returned SD messages */
1117 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1118 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1119 sd->msg[4] == ' ') {
1120 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1127 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1128 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1130 /* we print information on the slot if we have a volume name */
1132 /* Add Slot and VolumeName to list */
1133 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1135 vl->VolName = bstrdup(vol_name);
1136 vl->next = vol_list;
1140 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1141 ua->send_msg("D|%d||\n", Drive);
1143 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1144 content_send_info(ua, type, Slot, vol_name);
1146 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1147 /* type can be S (slot) or I (Import/Export slot) */
1148 vol_list_t *prev=NULL;
1149 for (vl = vol_list; vl; vl = vl->next) {
1150 if (vl->Slot == Slot) {
1151 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1153 /* remove the node */
1155 prev->next = vl->next;
1157 vol_list = vl->next;
1165 content_send_info(ua, type, Slot, vol_name);
1168 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1175 * Print slots from AutoChanger
1177 void status_slots(UAContext *ua, STORE *store_r)
1181 vol_list_t *vl, *vol_list = NULL;
1186 /* Slot | Volume | Status | MediaType | Pool */
1187 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1190 status_content(ua, store_r);
1194 if (!open_client_db(ua)) {
1197 store.store = store_r;
1199 pm_strcpy(store.store_source, _("command line"));
1200 set_wstorage(ua->jcr, &store);
1201 get_storage_drive(ua, store.store);
1203 max_slots = get_num_slots_from_SD(ua);
1205 if (max_slots <= 0) {
1206 ua->warning_msg(_("No slots in changer to scan.\n"));
1209 slot_list = (char *)malloc(max_slots+1);
1210 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1215 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1218 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1221 ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
1222 ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1224 /* Walk through the list getting the media records */
1225 for (vl=vol_list; vl; vl=vl->next) {
1226 if (vl->Slot > max_slots) {
1227 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1228 vl->Slot, max_slots);
1231 /* Check if user wants us to look at this slot */
1232 if (!slot_list[vl->Slot]) {
1233 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1237 slot_list[vl->Slot] = 0; /* clear Slot */
1240 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1241 ua->send_msg(slot_hformat,
1243 "?", "?", "?", "?");
1247 /* Hope that slots are ordered */
1248 for (; i < vl->Slot; i++) {
1250 ua->send_msg(slot_hformat,
1251 i, ' ', "", "", "", "");
1256 memset(&mr, 0, sizeof(MEDIA_DBR));
1257 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1259 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1260 memset(&pr, 0, sizeof(POOL_DBR));
1261 pr.PoolId = mr.PoolId;
1262 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1263 strcpy(pr.Name, "?");
1266 /* Print information */
1267 ua->send_msg(slot_hformat,
1268 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1269 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1271 } else { /* TODO: get information from catalog */
1272 ua->send_msg(slot_hformat,
1274 mr.VolumeName, "?", "?", "?");
1278 /* Display the rest of the autochanger
1280 for (; i <= max_slots; i++) {
1282 ua->send_msg(slot_hformat,
1283 i, ' ', "", "", "", "");
1290 free_vol_list(vol_list);