2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
18 * Bacula Director -- Tape labeling commands
20 * Kern Sibbald, April MMIII
27 /* Slot list definition */
28 typedef struct s_vol_list {
29 struct s_vol_list *next;
35 /* Forward referenced functions */
36 static int do_label(UAContext *ua, const char *cmd, int relabel);
37 static void label_from_barcodes(UAContext *ua, int drive);
38 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
39 POOL_DBR *pr, int relabel, bool media_record_exits, int drive);
40 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan);
41 static void free_vol_list(vol_list_t *vol_list);
42 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr);
43 BSOCK *open_sd_bsock(UAContext *ua);
44 void close_sd_bsock(UAContext *ua);
45 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive);
46 static int get_num_slots_from_SD(UAContext *ua);
52 * label storage=xxx volume=vvv
54 int label_cmd(UAContext *ua, const char *cmd)
56 return do_label(ua, cmd, 0); /* standard label */
59 int relabel_cmd(UAContext *ua, const char *cmd)
61 return do_label(ua, cmd, 1); /* relabel tape */
64 static bool get_user_slot_list(UAContext *ua, char *slot_list, int num_slots)
69 /* slots are numbered 1 to num_slots */
70 for (int i=0; i <= num_slots; i++) {
73 i = find_arg_with_value(ua, "slots");
74 if (i == -1) { /* not found */
75 i = find_arg_with_value(ua, "slot");
78 /* scan slot list in ua->argv[i] */
82 strip_trailing_junk(ua->argv[i]);
83 for (p=ua->argv[i]; p && *p; p=e) {
90 h = strchr(p, '-'); /* range? */
92 msg = _("Negative numbers not permitted\n");
97 if (!is_an_integer(h)) {
98 msg = _("Range end is not integer.\n");
102 if (!is_an_integer(p)) {
103 msg = _("Range start is not an integer.\n");
109 msg = _("Range end not bigger than start.\n");
114 if (!is_an_integer(p)) {
115 msg = _("Input value is not an integer.\n");
120 if (beg <= 0 || end <= 0) {
121 msg = _("Values must be be greater than zero.\n");
124 if (end > num_slots) {
125 msg = _("Slot too large.\n");
128 for (i=beg; i<=end; i++) {
129 slot_list[i] = 1; /* Turn on specified range */
133 /* Turn everything on */
134 for (i=1; i <= num_slots; i++) {
138 if (debug_level >= 100) {
139 Dmsg0(100, "Slots turned on:\n");
140 for (i=1; i <= num_slots; i++) {
142 Dmsg1(100, "%d\n", i);
149 Dmsg1(100, "Problem with user selection ERR=%s\n", msg);
154 * Update Slots corresponding to Volumes in autochanger
156 void update_slots(UAContext *ua)
159 vol_list_t *vl, *vol_list = NULL;
170 if (!open_client_db(ua)) {
173 store.store = get_storage_resource(ua, true/*arg is storage*/);
177 pm_strcpy(store.store_source, _("command line"));
178 set_wstorage(ua->jcr, &store);
179 drive = get_storage_drive(ua, store.store);
181 scan = find_arg(ua, NT_("scan")) >= 0;
182 if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
183 Enabled = get_enabled(ua, ua->argv[i]);
189 have_enabled = false;
192 max_slots = get_num_slots_from_SD(ua);
193 Dmsg1(100, "max_slots=%d\n", max_slots);
194 if (max_slots <= 0) {
195 ua->warning_msg(_("No slots in changer to scan.\n"));
198 slot_list = (char *)malloc(max_slots+1);
199 if (!get_user_slot_list(ua, slot_list, max_slots)) {
204 vol_list = get_vol_list_from_SD(ua, scan);
207 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
211 /* First zap out any InChanger with StorageId=0 */
212 db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);
214 /* Walk through the list updating the media records */
215 for (vl=vol_list; vl; vl=vl->next) {
216 if (vl->Slot > max_slots) {
217 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
218 vl->Slot, max_slots);
221 /* Check if user wants us to look at this slot */
222 if (!slot_list[vl->Slot]) {
223 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
226 /* If scanning, we read the label rather than the barcode */
232 vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
233 Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
235 slot_list[vl->Slot] = 0; /* clear Slot */
238 mr.MediaId = 0; /* Force using VolumeName */
240 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
242 mr.VolumeName[0] = 0;
244 set_storageid_in_mr(store.store, &mr);
245 Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
246 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
248 /* Set InChanger to zero for this Slot */
249 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
251 Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
252 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
254 Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
255 ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
259 Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
260 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
261 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
262 Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
263 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
264 /* If Slot, Inchanger, and StorageId have changed, update the Media record */
265 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
269 mr.Enabled = Enabled;
271 set_storageid_in_mr(store.store, &mr);
272 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
273 ua->error_msg("%s", db_strerror(ua->db));
276 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
277 mr.VolumeName, mr.Slot);
280 ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
286 ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
287 mr.VolumeName, vl->Slot);
293 set_storageid_in_mr(store.store, &mr);
295 for (int i=1; i <= max_slots; i++) {
298 /* Set InChanger to zero for this Slot */
299 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
306 free_vol_list(vol_list);
315 * Common routine for both label and relabel
317 static int do_label(UAContext *ua, const char *cmd, int relabel)
321 char dev_name[MAX_NAME_LENGTH];
324 bool print_reminder = true;
325 bool label_barcodes = false;
329 bool media_record_exists = false;
330 static const char *barcode_keyword[] = {
336 memset(&pr, 0, sizeof(pr));
337 if (!open_client_db(ua)) {
341 /* Look for one of the barcode keywords */
342 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
343 /* Now find the keyword in the list */
344 if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
345 *ua->argk[j] = 0; /* zap barcode keyword */
347 label_barcodes = true;
350 store.store = get_storage_resource(ua, true/*use default*/);
354 pm_strcpy(store.store_source, _("command line"));
355 set_wstorage(ua->jcr, &store);
356 drive = get_storage_drive(ua, store.store);
358 if (label_barcodes) {
359 label_from_barcodes(ua, drive);
363 /* If relabel get name of Volume to relabel */
365 /* Check for oldvolume=name */
366 i = find_arg_with_value(ua, "oldvolume");
368 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
370 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
373 ua->error_msg("%s", db_strerror(ua->db));
375 /* No keyword or Vol not found, ask user to select */
376 if (!select_media_dbr(ua, &omr)) {
380 /* Require Volume to be Purged or Recycled */
382 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
383 ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
384 omr.VolumeName, omr.VolStatus);
389 /* Check for volume=NewVolume */
390 i = find_arg_with_value(ua, "volume");
392 pm_strcpy(ua->cmd, ua->argv[i]);
396 /* Get a new Volume name */
398 media_record_exists = false;
399 if (!get_cmd(ua, _("Enter new Volume name: "))) {
403 if (!is_volume_name_legal(ua, ua->cmd)) {
407 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
409 /* If VolBytes are zero the Volume is not labeled */
410 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
411 if (mr.VolBytes != 0) {
412 ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
416 media_record_exists = true;
421 /* If autochanger, request slot */
422 i = find_arg_with_value(ua, "slot");
424 mr.Slot = atoi(ua->argv[i]);
428 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
429 } else if (store.store->autochanger) {
430 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
433 mr.Slot = ua->pint32_val;
437 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
439 set_storageid_in_mr(store.store, &mr);
441 bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
443 /* Must select Pool if not already done */
444 if (pr.PoolId == 0) {
445 memset(&pr, 0, sizeof(pr));
446 if (!select_pool_dbr(ua, &pr)) {
451 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
454 sd = ua->jcr->store_bsock;
456 /* Delete the old media record */
457 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
458 ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
459 omr.VolumeName, db_strerror(ua->db));
461 ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"),
463 /* Update the number of Volumes in the pool */
465 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
466 ua->error_msg("%s", db_strerror(ua->db));
471 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
472 ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
473 bash_spaces(dev_name);
474 sd->fsend("mount %s drive=%d", dev_name, drive);
475 unbash_spaces(dev_name);
476 while (sd->recv() >= 0) {
477 ua->send_msg("%s", sd->msg);
479 * 3001 OK mount. Device=xxx or
480 * 3001 Mounted Volume vvvv
481 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
482 * 3906 is cannot mount non-tape
483 * So for those, no need to print a reminder
485 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
486 strncmp(sd->msg, "3002 ", 5) == 0 ||
487 strncmp(sd->msg, "3906 ", 5) == 0) {
488 print_reminder = false;
493 if (print_reminder) {
494 ua->info_msg(_("Do not forget to mount the drive!!!\n"));
502 * Request SD to send us the slot:barcodes, then wiffle
503 * through them all labeling them.
505 static void label_from_barcodes(UAContext *ua, int drive)
507 STORE *store = ua->jcr->wstore;
510 vol_list_t *vl, *vol_list = NULL;
511 bool media_record_exists;
516 max_slots = get_num_slots_from_SD(ua);
517 if (max_slots <= 0) {
518 ua->warning_msg(_("No slots in changer to scan.\n"));
521 slot_list = (char *)malloc(max_slots+1);
522 if (!get_user_slot_list(ua, slot_list, max_slots)) {
526 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
529 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
533 /* Display list of Volumes and ask if he really wants to proceed */
534 ua->send_msg(_("The following Volumes will be labeled:\n"
536 "==============\n"));
537 for (vl=vol_list; vl; vl=vl->next) {
538 if (!vl->VolName || !slot_list[vl->Slot]) {
541 ua->send_msg("%4d %s\n", vl->Slot, vl->VolName);
543 if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
544 (ua->pint32_val == 0)) {
548 memset(&pr, 0, sizeof(pr));
549 if (!select_pool_dbr(ua, &pr)) {
553 /* Fire off the label requests */
554 for (vl=vol_list; vl; vl=vl->next) {
555 if (!vl->VolName || !slot_list[vl->Slot]) {
559 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
560 media_record_exists = false;
561 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
562 if (mr.VolBytes != 0) {
563 ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
564 vl->Slot, mr.VolumeName);
566 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
567 set_storageid_in_mr(store, &mr);
568 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
569 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
573 media_record_exists = true;
575 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
576 set_storageid_in_mr(store, &mr);
578 * Deal with creating cleaning tape here. Normal tapes created in
579 * send_label_request() below
581 if (is_cleaning_tape(ua, &mr, &pr)) {
582 if (media_record_exists) { /* we update it */
583 mr.VolBytes = 1; /* any bytes to indicate it exists */
584 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
586 set_storageid_in_mr(store, &mr);
587 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
588 ua->error_msg("%s", db_strerror(ua->db));
590 } else { /* create the media record */
591 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
592 ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
595 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
596 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
598 set_storageid_in_mr(store, &mr);
599 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
600 ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
602 pr.NumVols++; /* this is a bit suspect */
603 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
604 ua->error_msg("%s", db_strerror(ua->db));
607 ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
610 continue; /* done, go handle next volume */
612 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
615 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
621 free_vol_list(vol_list);
628 * Check if the Volume name has legal characters
629 * If ua is non-NULL send the message
631 bool is_volume_name_legal(UAContext *ua, const char *name)
635 const char *accept = ":.-_";
637 /* Restrict the characters permitted in the Volume name */
638 for (p=name; *p; p++) {
639 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
643 ua->error_msg(_("Illegal character \"%c\" in a volume name.\n"), *p);
648 if (len >= MAX_NAME_LENGTH) {
650 ua->error_msg(_("Volume name too long.\n"));
656 ua->error_msg(_("Volume name must be at least one character long.\n"));
664 * NOTE! This routine opens the SD socket but leaves it open
666 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
667 POOL_DBR *pr, int relabel, bool media_record_exists,
671 char dev_name[MAX_NAME_LENGTH];
674 uint64_t VolBytes = 0;
676 if (!(sd=open_sd_bsock(ua))) {
679 bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
680 bash_spaces(dev_name);
681 bash_spaces(mr->VolumeName);
682 bash_spaces(mr->MediaType);
683 bash_spaces(pr->Name);
685 bash_spaces(omr->VolumeName);
686 sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
687 "MediaType=%s Slot=%d drive=%d",
688 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
689 mr->MediaType, mr->Slot, drive);
690 ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
691 omr->VolumeName, mr->VolumeName);
693 sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
695 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
697 ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"),
698 mr->VolumeName, mr->Slot);
699 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
700 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
703 while (sd->recv() >= 0) {
705 ua->send_msg("%s", sd->msg);
706 if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes,
712 unbash_spaces(mr->VolumeName);
713 unbash_spaces(mr->MediaType);
714 unbash_spaces(pr->Name);
715 mr->LabelDate = time(NULL);
716 mr->set_label_date = true;
718 /* We know that a freshly labelled DVD has 1 VolParts */
719 /* This does not apply to auto-labelled DVDs. */
723 if (media_record_exists) { /* we update it */
724 mr->VolBytes = VolBytes;
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->InChanger = mr->Slot > 0; /* if slot give assume in changer */
736 set_storageid_in_mr(ua->jcr->wstore, mr);
737 if (db_create_media_record(ua->jcr, ua->db, mr)) {
738 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
739 mr->VolumeName, mr->Slot);
740 /* Update number of volumes in pool */
742 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
743 ua->error_msg("%s", db_strerror(ua->db));
746 ua->error_msg("%s", db_strerror(ua->db));
751 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
756 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
758 STORE *store = ua->jcr->wstore;
760 char dev_name[MAX_NAME_LENGTH];
761 char *VolName = NULL;
764 if (!(sd=open_sd_bsock(ua))) {
765 ua->error_msg(_("Could not open SD socket.\n"));
768 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
769 bash_spaces(dev_name);
770 /* Ask for autochanger list of volumes */
771 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
772 Dmsg1(100, "Sent: %s", sd->msg);
774 /* Get Volume name in this Slot */
775 while (sd->recv() >= 0) {
776 ua->send_msg("%s", sd->msg);
777 Dmsg1(100, "Got: %s", sd->msg);
778 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
779 VolName = (char *)malloc(sd->msglen);
780 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
788 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
793 * We get the slot list from the Storage daemon.
794 * If scan is set, we return all slots found,
795 * otherwise, we return only slots with valid barcodes (Volume names)
797 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
799 STORE *store = ua->jcr->wstore;
800 char dev_name[MAX_NAME_LENGTH];
803 vol_list_t *vol_list = NULL;
806 if (!(sd=open_sd_bsock(ua))) {
810 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
811 bash_spaces(dev_name);
812 /* Ask for autochanger list of volumes */
813 sd->fsend(NT_("autochanger list %s \n"), dev_name);
815 /* Read and organize list of Volumes */
816 while (sd->recv() >= 0) {
819 strip_trailing_junk(sd->msg);
821 /* Check for returned SD messages */
822 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
823 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
825 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
829 /* Validate Slot: if scanning, otherwise Slot:Barcode */
830 p = strchr(sd->msg, ':');
832 /* Scanning -- require only valid slot */
833 Slot = atoi(sd->msg);
837 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
842 if (p && strlen(p) > 1) {
844 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
847 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
853 if (!is_volume_name_legal(ua, p)) {
856 ua->error_msg(_("Invalid Volume name: %s\n"), sd->msg);
861 /* Add Slot and VolumeName to list */
862 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
866 p++; /* skip separator */
868 vl->VolName = bstrdup(p);
872 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
877 vol_list_t *prev=vol_list;
878 /* Add new entry to the right place in the list */
879 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
880 if (tvl->Slot > vl->Slot) {
881 /* no previous item, update vol_list directly */
882 if (prev == vol_list) {
886 } else { /* replace the previous pointer */
892 /* we are at the end */
906 static void free_vol_list(vol_list_t *vol_list)
911 for (vl=vol_list; vl; ) {
923 * We get the number of slots in the changer from the SD
925 static int get_num_slots_from_SD(UAContext *ua)
927 STORE *store = ua->jcr->wstore;
928 char dev_name[MAX_NAME_LENGTH];
933 if (!(sd=open_sd_bsock(ua))) {
937 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
938 bash_spaces(dev_name);
939 /* Ask for autochanger number of slots */
940 sd->fsend(NT_("autochanger slots %s\n"), dev_name);
942 while (sd->recv() >= 0) {
943 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
946 ua->send_msg("%s", sd->msg);
950 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
955 * We get the number of drives in the changer from the SD
957 int get_num_drives_from_SD(UAContext *ua)
959 STORE *store = ua->jcr->wstore;
960 char dev_name[MAX_NAME_LENGTH];
965 if (!(sd=open_sd_bsock(ua))) {
969 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
970 bash_spaces(dev_name);
971 /* Ask for autochanger number of slots */
972 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
974 while (sd->recv() >= 0) {
975 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
978 ua->send_msg("%s", sd->msg);
982 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
987 * Check if this is a cleaning tape by comparing the Volume name
988 * with the Cleaning Prefix. If they match, this is a cleaning
991 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
993 /* Find Pool resource */
994 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
995 if (!ua->jcr->pool) {
996 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
997 pr->Name, mr->VolumeName);
1000 if (ua->jcr->pool->cleaning_prefix == NULL) {
1003 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1004 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1005 strlen(ua->jcr->pool->cleaning_prefix),
1006 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1007 strlen(ua->jcr->pool->cleaning_prefix)));
1008 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1009 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1012 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1014 char ed1[50], ed2[50], ed3[50];
1017 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1018 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1019 const char *slot_api_empty_format="%c|%i||||||||\n";
1021 if (is_volume_name_legal(NULL, vol_name)) {
1022 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1023 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1024 memset(&pr, 0, sizeof(POOL_DBR));
1025 pr.PoolId = mr.PoolId;
1026 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1027 strcpy(pr.Name, "?");
1029 ua->send_msg(slot_api_full_format, type,
1030 Slot, mr.Slot, mr.VolumeName,
1031 edit_uint64(mr.VolBytes, ed1),
1032 mr.VolStatus, mr.MediaType, pr.Name,
1033 edit_uint64(mr.LastWritten, ed2),
1034 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1036 } else { /* Media unknown */
1037 ua->send_msg(slot_api_full_format,
1038 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1043 ua->send_msg(slot_api_empty_format, type, Slot);
1048 * Input (output of mxt-changer listall):
1050 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1051 * D:0:F:2:vol2 or D:Drive num:E
1056 * S:1:F:vol1 S:Slot num:F:Volume Name
1057 * S:2:E or S:Slot num:E
1060 * Import/Export tray slots:
1061 * I:10:F:vol10 I:Slot num:F:Volume Name
1062 * I:11:E or I:Slot num:E
1065 * If a drive is loaded, the slot *should* be empty
1069 * Drive list: D|Drive num|Slot loaded|Volume Name
1074 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1076 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1078 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1080 * TODO: need to merge with status_slots()
1082 void status_content(UAContext *ua, STORE *store)
1086 char dev_name[MAX_NAME_LENGTH];
1087 char vol_name[MAX_NAME_LENGTH];
1089 vol_list_t *vl=NULL, *vol_list = NULL;
1091 if (!(sd=open_sd_bsock(ua))) {
1095 if (!open_client_db(ua)) {
1099 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1100 bash_spaces(dev_name);
1101 /* Ask for autochanger list of volumes */
1102 sd->fsend(NT_("autochanger listall %s \n"), dev_name);
1104 /* Read and organize list of Drive, Slots and I/O Slots */
1105 while (sd->recv() >= 0) {
1106 strip_trailing_junk(sd->msg);
1108 /* Check for returned SD messages */
1109 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1110 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1111 sd->msg[4] == ' ') {
1112 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1119 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1120 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1122 /* we print information on the slot if we have a volume name */
1124 /* Add Slot and VolumeName to list */
1125 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1127 vl->VolName = bstrdup(vol_name);
1128 vl->next = vol_list;
1132 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1133 ua->send_msg("D|%d||\n", Drive);
1135 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1136 content_send_info(ua, type, Slot, vol_name);
1138 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1139 /* type can be S (slot) or I (Import/Export slot) */
1140 vol_list_t *prev=NULL;
1141 for (vl = vol_list; vl; vl = vl->next) {
1142 if (vl->Slot == Slot) {
1143 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1145 /* remove the node */
1147 prev->next = vl->next;
1149 vol_list = vl->next;
1157 content_send_info(ua, type, Slot, vol_name);
1160 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1167 * Print slots from AutoChanger
1169 void status_slots(UAContext *ua, STORE *store_r)
1173 vol_list_t *vl, *vol_list = NULL;
1178 /* Slot | Volume | Status | MediaType | Pool */
1179 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1182 status_content(ua, store_r);
1186 if (!open_client_db(ua)) {
1189 store.store = store_r;
1191 pm_strcpy(store.store_source, _("command line"));
1192 set_wstorage(ua->jcr, &store);
1193 get_storage_drive(ua, store.store);
1195 max_slots = get_num_slots_from_SD(ua);
1197 if (max_slots <= 0) {
1198 ua->warning_msg(_("No slots in changer to scan.\n"));
1201 slot_list = (char *)malloc(max_slots+1);
1202 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1207 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1210 ua->warning_msg(_("No Volumes found, or no barcodes.\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, ' ', "", "", "", "");
1248 memset(&mr, 0, sizeof(MEDIA_DBR));
1249 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1251 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1252 memset(&pr, 0, sizeof(POOL_DBR));
1253 pr.PoolId = mr.PoolId;
1254 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1255 strcpy(pr.Name, "?");
1258 /* Print information */
1259 ua->send_msg(slot_hformat,
1260 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1261 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1263 } else { /* TODO: get information from catalog */
1264 ua->send_msg(slot_hformat,
1266 mr.VolumeName, "?", "?", "?");
1270 /* Display the rest of the autochanger
1272 for (; i <= max_slots; i++) {
1274 ua->send_msg(slot_hformat,
1275 i, ' ', "", "", "", "");
1282 free_vol_list(vol_list);