2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2012 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- Tape labeling commands
32 * Kern Sibbald, April MMIII
39 /* Slot list definition */
40 typedef struct s_vol_list {
41 struct s_vol_list *next;
47 /* Forward referenced functions */
48 static int do_label(UAContext *ua, const char *cmd, int relabel);
49 static void label_from_barcodes(UAContext *ua, int drive);
50 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
51 POOL_DBR *pr, int relabel, bool media_record_exits, int drive);
52 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan);
53 static void free_vol_list(vol_list_t *vol_list);
54 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr);
55 static BSOCK *open_sd_bsock(UAContext *ua);
56 static void close_sd_bsock(UAContext *ua);
57 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive);
58 static int get_num_slots_from_SD(UAContext *ua);
64 * label storage=xxx volume=vvv
66 int label_cmd(UAContext *ua, const char *cmd)
68 return do_label(ua, cmd, 0); /* standard label */
71 int relabel_cmd(UAContext *ua, const char *cmd)
73 return do_label(ua, cmd, 1); /* relabel tape */
76 static bool get_user_slot_list(UAContext *ua, char *slot_list, int num_slots)
81 /* slots are numbered 1 to num_slots */
82 for (int i=0; i <= num_slots; i++) {
85 i = find_arg_with_value(ua, "slots");
86 if (i == -1) { /* not found */
87 i = find_arg_with_value(ua, "slot");
90 /* scan slot list in ua->argv[i] */
94 strip_trailing_junk(ua->argv[i]);
95 for (p=ua->argv[i]; p && *p; p=e) {
101 /* Check for range */
102 h = strchr(p, '-'); /* range? */
104 msg = _("Negative numbers not permitted\n");
109 if (!is_an_integer(h)) {
110 msg = _("Range end is not integer.\n");
114 if (!is_an_integer(p)) {
115 msg = _("Range start is not an integer.\n");
121 msg = _("Range end not bigger than start.\n");
126 if (!is_an_integer(p)) {
127 msg = _("Input value is not an integer.\n");
132 if (beg <= 0 || end <= 0) {
133 msg = _("Values must be be greater than zero.\n");
136 if (end > num_slots) {
137 msg = _("Slot too large.\n");
140 for (i=beg; i<=end; i++) {
141 slot_list[i] = 1; /* Turn on specified range */
145 /* Turn everything on */
146 for (i=1; i <= num_slots; i++) {
150 Dmsg0(100, "Slots turned on:\n");
151 for (i=1; i <= num_slots; i++) {
153 Dmsg1(100, "%d\n", i);
159 Dmsg1(100, "Problem with user selection ERR=%s\n", msg);
164 * Update Slots corresponding to Volumes in autochanger
166 void update_slots(UAContext *ua)
169 vol_list_t *vl, *vol_list = NULL;
180 if (!open_client_db(ua)) {
183 store.store = get_storage_resource(ua, true/*arg is storage*/);
187 pm_strcpy(store.store_source, _("command line"));
188 set_wstorage(ua->jcr, &store);
189 drive = get_storage_drive(ua, store.store);
191 scan = find_arg(ua, NT_("scan")) >= 0;
192 if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
193 Enabled = get_enabled(ua, ua->argv[i]);
199 have_enabled = false;
202 max_slots = get_num_slots_from_SD(ua);
203 Dmsg1(100, "max_slots=%d\n", max_slots);
204 if (max_slots <= 0) {
205 ua->warning_msg(_("No slots in changer to scan.\n"));
208 slot_list = (char *)malloc(max_slots+1);
209 if (!get_user_slot_list(ua, slot_list, max_slots)) {
214 vol_list = get_vol_list_from_SD(ua, scan);
217 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
221 /* First zap out any InChanger with StorageId=0 */
222 db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);
224 /* Walk through the list updating the media records */
225 for (vl=vol_list; vl; vl=vl->next) {
226 if (vl->Slot > max_slots) {
227 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
228 vl->Slot, max_slots);
231 /* Check if user wants us to look at this slot */
232 if (!slot_list[vl->Slot]) {
233 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
236 /* If scanning, we read the label rather than the barcode */
242 vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
243 Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
245 slot_list[vl->Slot] = 0; /* clear Slot */
248 set_storageid_in_mr(store.store, &mr);
249 /* Set InChanger to zero for this Slot */
250 /**** ***FIXME**** */
252 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
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 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
261 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
262 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
266 mr.Enabled = Enabled;
268 set_storageid_in_mr(store.store, &mr);
269 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
270 ua->error_msg("%s", db_strerror(ua->db));
273 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
274 mr.VolumeName, mr.Slot);
277 ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
283 ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
284 mr.VolumeName, vl->Slot);
290 set_storageid_in_mr(store.store, &mr);
292 for (int i=1; i <= max_slots; i++) {
295 /* Set InChanger to zero for this Slot */
296 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
303 free_vol_list(vol_list);
312 * Common routine for both label and relabel
314 static int do_label(UAContext *ua, const char *cmd, int relabel)
318 char dev_name[MAX_NAME_LENGTH];
321 bool print_reminder = true;
322 bool label_barcodes = false;
326 bool media_record_exists = false;
327 static const char *barcode_keyword[] = {
333 memset(&pr, 0, sizeof(pr));
334 if (!open_client_db(ua)) {
338 /* Look for one of the barcode keywords */
339 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
340 /* Now find the keyword in the list */
341 if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
342 *ua->argk[j] = 0; /* zap barcode keyword */
344 label_barcodes = true;
347 store.store = get_storage_resource(ua, true/*use default*/);
351 pm_strcpy(store.store_source, _("command line"));
352 set_wstorage(ua->jcr, &store);
353 drive = get_storage_drive(ua, store.store);
355 if (label_barcodes) {
356 label_from_barcodes(ua, drive);
360 /* If relabel get name of Volume to relabel */
362 /* Check for oldvolume=name */
363 i = find_arg_with_value(ua, "oldvolume");
365 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
366 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
369 ua->error_msg("%s", db_strerror(ua->db));
371 /* No keyword or Vol not found, ask user to select */
372 if (!select_media_dbr(ua, &omr)) {
376 /* Require Volume to be Purged or Recycled */
378 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
379 ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
380 omr.VolumeName, omr.VolStatus);
385 /* Check for volume=NewVolume */
386 i = find_arg_with_value(ua, "volume");
388 pm_strcpy(ua->cmd, ua->argv[i]);
392 /* Get a new Volume name */
394 media_record_exists = false;
395 if (!get_cmd(ua, _("Enter new Volume name: "))) {
399 if (!is_volume_name_legal(ua, ua->cmd)) {
403 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
404 /* If VolBytes are zero the Volume is not labeled */
405 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
406 if (mr.VolBytes != 0) {
407 ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
411 media_record_exists = true;
416 /* If autochanger, request slot */
417 i = find_arg_with_value(ua, "slot");
419 mr.Slot = atoi(ua->argv[i]);
423 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
424 } else if (store.store->autochanger) {
425 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
428 mr.Slot = ua->pint32_val;
432 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
434 set_storageid_in_mr(store.store, &mr);
436 bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
438 /* Must select Pool if not already done */
439 if (pr.PoolId == 0) {
440 memset(&pr, 0, sizeof(pr));
441 if (!select_pool_dbr(ua, &pr)) {
446 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
449 sd = ua->jcr->store_bsock;
451 /* Delete the old media record */
452 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
453 ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
454 omr.VolumeName, db_strerror(ua->db));
456 ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"),
458 /* Update the number of Volumes in the pool */
460 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
461 ua->error_msg("%s", db_strerror(ua->db));
466 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
467 ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
468 bash_spaces(dev_name);
469 bnet_fsend(sd, "mount %s drive=%d", dev_name, drive);
470 unbash_spaces(dev_name);
471 while (bnet_recv(sd) >= 0) {
472 ua->send_msg("%s", sd->msg);
474 * 3001 OK mount. Device=xxx or
475 * 3001 Mounted Volume vvvv
476 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
477 * 3906 is cannot mount non-tape
478 * So for those, no need to print a reminder
480 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
481 strncmp(sd->msg, "3002 ", 5) == 0 ||
482 strncmp(sd->msg, "3906 ", 5) == 0) {
483 print_reminder = false;
488 if (print_reminder) {
489 ua->info_msg(_("Do not forget to mount the drive!!!\n"));
497 * Request SD to send us the slot:barcodes, then wiffle
498 * through them all labeling them.
500 static void label_from_barcodes(UAContext *ua, int drive)
502 STORE *store = ua->jcr->wstore;
505 vol_list_t *vl, *vol_list = NULL;
506 bool media_record_exists;
511 max_slots = get_num_slots_from_SD(ua);
512 if (max_slots <= 0) {
513 ua->warning_msg(_("No slots in changer to scan.\n"));
516 slot_list = (char *)malloc(max_slots+1);
517 if (!get_user_slot_list(ua, slot_list, max_slots)) {
521 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
524 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
528 /* Display list of Volumes and ask if he really wants to proceed */
529 ua->send_msg(_("The following Volumes will be labeled:\n"
531 "==============\n"));
532 for (vl=vol_list; vl; vl=vl->next) {
533 if (!vl->VolName || !slot_list[vl->Slot]) {
536 ua->send_msg("%4d %s\n", vl->Slot, vl->VolName);
538 if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
539 (ua->pint32_val == 0)) {
543 memset(&pr, 0, sizeof(pr));
544 if (!select_pool_dbr(ua, &pr)) {
548 /* Fire off the label requests */
549 for (vl=vol_list; vl; vl=vl->next) {
550 if (!vl->VolName || !slot_list[vl->Slot]) {
554 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
555 media_record_exists = false;
556 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
557 if (mr.VolBytes != 0) {
558 ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
559 vl->Slot, mr.VolumeName);
561 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
562 set_storageid_in_mr(store, &mr);
563 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
564 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
568 media_record_exists = true;
570 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
571 set_storageid_in_mr(store, &mr);
573 * Deal with creating cleaning tape here. Normal tapes created in
574 * send_label_request() below
576 if (is_cleaning_tape(ua, &mr, &pr)) {
577 if (media_record_exists) { /* we update it */
578 mr.VolBytes = 1; /* any bytes to indicate it exists */
579 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
581 set_storageid_in_mr(store, &mr);
582 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
583 ua->error_msg("%s", db_strerror(ua->db));
585 } else { /* create the media record */
586 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
587 ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
590 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
591 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
593 set_storageid_in_mr(store, &mr);
594 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
595 ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
597 pr.NumVols++; /* this is a bit suspect */
598 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
599 ua->error_msg("%s", db_strerror(ua->db));
602 ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
605 continue; /* done, go handle next volume */
607 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
610 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
616 free_vol_list(vol_list);
623 * Check if the Volume name has legal characters
624 * If ua is non-NULL send the message
626 bool is_volume_name_legal(UAContext *ua, const char *name)
630 const char *accept = ":.-_";
632 /* Restrict the characters permitted in the Volume name */
633 for (p=name; *p; p++) {
634 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
638 ua->error_msg(_("Illegal character \"%c\" in a volume name.\n"), *p);
643 if (len >= MAX_NAME_LENGTH) {
645 ua->error_msg(_("Volume name too long.\n"));
651 ua->error_msg(_("Volume name must be at least one character long.\n"));
659 * NOTE! This routine opens the SD socket but leaves it open
661 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
662 POOL_DBR *pr, int relabel, bool media_record_exists,
666 char dev_name[MAX_NAME_LENGTH];
669 uint64_t VolBytes = 0;
671 if (!(sd=open_sd_bsock(ua))) {
674 bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
675 bash_spaces(dev_name);
676 bash_spaces(mr->VolumeName);
677 bash_spaces(mr->MediaType);
678 bash_spaces(pr->Name);
680 bash_spaces(omr->VolumeName);
681 sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
682 "MediaType=%s Slot=%d drive=%d",
683 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
684 mr->MediaType, mr->Slot, drive);
685 ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
686 omr->VolumeName, mr->VolumeName);
688 sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
690 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
692 ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"),
693 mr->VolumeName, mr->Slot);
694 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
695 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
698 while (sd->recv() >= 0) {
700 ua->send_msg("%s", sd->msg);
701 if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes,
707 unbash_spaces(mr->VolumeName);
708 unbash_spaces(mr->MediaType);
709 unbash_spaces(pr->Name);
710 mr->LabelDate = time(NULL);
711 mr->set_label_date = true;
713 /* We know that a freshly labelled DVD has 1 VolParts */
714 /* This does not apply to auto-labelled DVDs. */
718 if (media_record_exists) { /* we update it */
719 mr->VolBytes = VolBytes;
720 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
721 set_storageid_in_mr(ua->jcr->wstore, mr);
722 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
723 ua->error_msg("%s", db_strerror(ua->db));
726 } else { /* create the media record */
727 set_pool_dbr_defaults_in_media_dbr(mr, pr);
728 mr->VolBytes = VolBytes;
729 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
731 set_storageid_in_mr(ua->jcr->wstore, mr);
732 if (db_create_media_record(ua->jcr, ua->db, mr)) {
733 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
734 mr->VolumeName, mr->Slot);
735 /* Update number of volumes in pool */
737 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
738 ua->error_msg("%s", db_strerror(ua->db));
741 ua->error_msg("%s", db_strerror(ua->db));
746 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
751 static BSOCK *open_sd_bsock(UAContext *ua)
753 STORE *store = ua->jcr->wstore;
755 if (!ua->jcr->store_bsock) {
756 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d ...\n"),
757 store->name(), store->address, store->SDport);
758 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
759 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
763 return ua->jcr->store_bsock;
766 static void close_sd_bsock(UAContext *ua)
768 if (ua->jcr->store_bsock) {
769 bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
770 bnet_close(ua->jcr->store_bsock);
771 ua->jcr->store_bsock = NULL;
775 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
777 STORE *store = ua->jcr->wstore;
779 char dev_name[MAX_NAME_LENGTH];
780 char *VolName = NULL;
783 if (!(sd=open_sd_bsock(ua))) {
784 ua->error_msg(_("Could not open SD socket.\n"));
787 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
788 bash_spaces(dev_name);
789 /* Ask for autochanger list of volumes */
790 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
791 Dmsg1(100, "Sent: %s", sd->msg);
793 /* Get Volume name in this Slot */
794 while (sd->recv() >= 0) {
795 ua->send_msg("%s", sd->msg);
796 Dmsg1(100, "Got: %s", sd->msg);
797 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
798 VolName = (char *)malloc(sd->msglen);
799 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
807 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
812 * We get the slot list from the Storage daemon.
813 * If scan is set, we return all slots found,
814 * otherwise, we return only slots with valid barcodes (Volume names)
816 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
818 STORE *store = ua->jcr->wstore;
819 char dev_name[MAX_NAME_LENGTH];
822 vol_list_t *vol_list = NULL;
825 if (!(sd=open_sd_bsock(ua))) {
829 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
830 bash_spaces(dev_name);
831 /* Ask for autochanger list of volumes */
832 bnet_fsend(sd, NT_("autochanger list %s \n"), dev_name);
834 /* Read and organize list of Volumes */
835 while (bnet_recv(sd) >= 0) {
838 strip_trailing_junk(sd->msg);
840 /* Check for returned SD messages */
841 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
842 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
844 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
848 /* Validate Slot: if scanning, otherwise Slot:Barcode */
849 p = strchr(sd->msg, ':');
851 /* Scanning -- require only valid slot */
852 Slot = atoi(sd->msg);
856 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
861 if (p && strlen(p) > 1) {
863 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
866 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
872 if (!is_volume_name_legal(ua, p)) {
875 ua->error_msg(_("Invalid Volume name: %s\n"), sd->msg);
880 /* Add Slot and VolumeName to list */
881 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
885 p++; /* skip separator */
887 vl->VolName = bstrdup(p);
891 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
896 vol_list_t *prev=vol_list;
897 /* Add new entry to the right place in the list */
898 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
899 if (tvl->Slot > vl->Slot) {
900 /* no previous item, update vol_list directly */
901 if (prev == vol_list) {
905 } else { /* replace the previous pointer */
911 /* we are at the end */
925 static void free_vol_list(vol_list_t *vol_list)
930 for (vl=vol_list; vl; ) {
942 * We get the number of slots in the changer from the SD
944 static int get_num_slots_from_SD(UAContext *ua)
946 STORE *store = ua->jcr->wstore;
947 char dev_name[MAX_NAME_LENGTH];
952 if (!(sd=open_sd_bsock(ua))) {
956 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
957 bash_spaces(dev_name);
958 /* Ask for autochanger number of slots */
959 sd->fsend(NT_("autochanger slots %s\n"), dev_name);
961 while (sd->recv() >= 0) {
962 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
965 ua->send_msg("%s", sd->msg);
969 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
974 * We get the number of drives in the changer from the SD
976 int get_num_drives_from_SD(UAContext *ua)
978 STORE *store = ua->jcr->wstore;
979 char dev_name[MAX_NAME_LENGTH];
984 if (!(sd=open_sd_bsock(ua))) {
988 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
989 bash_spaces(dev_name);
990 /* Ask for autochanger number of slots */
991 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
993 while (sd->recv() >= 0) {
994 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
997 ua->send_msg("%s", sd->msg);
1001 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
1006 * Check if this is a cleaning tape by comparing the Volume name
1007 * with the Cleaning Prefix. If they match, this is a cleaning
1010 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
1012 /* Find Pool resource */
1013 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
1014 if (!ua->jcr->pool) {
1015 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
1016 pr->Name, mr->VolumeName);
1019 if (ua->jcr->pool->cleaning_prefix == NULL) {
1022 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1023 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1024 strlen(ua->jcr->pool->cleaning_prefix),
1025 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1026 strlen(ua->jcr->pool->cleaning_prefix)));
1027 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1028 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1031 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1033 char ed1[50], ed2[50], ed3[50];
1036 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1037 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1038 const char *slot_api_empty_format="%c|%i||||||||\n";
1040 if (is_volume_name_legal(NULL, vol_name)) {
1041 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1042 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1043 memset(&pr, 0, sizeof(POOL_DBR));
1044 pr.PoolId = mr.PoolId;
1045 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1046 strcpy(pr.Name, "?");
1048 ua->send_msg(slot_api_full_format, type,
1049 Slot, mr.Slot, mr.VolumeName,
1050 edit_uint64(mr.VolBytes, ed1),
1051 mr.VolStatus, mr.MediaType, pr.Name,
1052 edit_uint64(mr.LastWritten, ed2),
1053 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1055 } else { /* Media unknown */
1056 ua->send_msg(slot_api_full_format,
1057 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1062 ua->send_msg(slot_api_empty_format, type, Slot);
1067 * Input (output of mxt-changer listall):
1069 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1070 * D:0:F:2:vol2 or D:Drive num:E
1075 * S:1:F:vol1 S:Slot num:F:Volume Name
1076 * S:2:E or S:Slot num:E
1079 * Import/Export tray slots:
1080 * I:10:F:vol10 I:Slot num:F:Volume Name
1081 * I:11:E or I:Slot num:E
1084 * If a drive is loaded, the slot *should* be empty
1088 * Drive list: D|Drive num|Slot loaded|Volume Name
1093 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1095 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1097 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1099 * TODO: need to merge with status_slots()
1101 void status_content(UAContext *ua, STORE *store)
1105 char dev_name[MAX_NAME_LENGTH];
1106 char vol_name[MAX_NAME_LENGTH];
1108 vol_list_t *vl=NULL, *vol_list = NULL;
1110 if (!(sd=open_sd_bsock(ua))) {
1114 if (!open_client_db(ua)) {
1118 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1119 bash_spaces(dev_name);
1120 /* Ask for autochanger list of volumes */
1121 bnet_fsend(sd, NT_("autochanger listall %s \n"), dev_name);
1123 /* Read and organize list of Drive, Slots and I/O Slots */
1124 while (bnet_recv(sd) >= 0) {
1125 strip_trailing_junk(sd->msg);
1127 /* Check for returned SD messages */
1128 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1129 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1130 sd->msg[4] == ' ') {
1131 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1138 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1139 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1141 /* we print information on the slot if we have a volume name */
1143 /* Add Slot and VolumeName to list */
1144 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1146 vl->VolName = bstrdup(vol_name);
1147 vl->next = vol_list;
1151 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1152 ua->send_msg("D|%d||\n", Drive);
1154 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1155 content_send_info(ua, type, Slot, vol_name);
1157 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1158 /* type can be S (slot) or I (Import/Export slot) */
1159 vol_list_t *prev=NULL;
1160 for (vl = vol_list; vl; vl = vl->next) {
1161 if (vl->Slot == Slot) {
1162 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1164 /* remove the node */
1166 prev->next = vl->next;
1168 vol_list = vl->next;
1176 content_send_info(ua, type, Slot, vol_name);
1179 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1186 * Print slots from AutoChanger
1188 void status_slots(UAContext *ua, STORE *store_r)
1192 vol_list_t *vl, *vol_list = NULL;
1197 /* Slot | Volume | Status | MediaType | Pool */
1198 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1201 status_content(ua, store_r);
1205 if (!open_client_db(ua)) {
1208 store.store = store_r;
1210 pm_strcpy(store.store_source, _("command line"));
1211 set_wstorage(ua->jcr, &store);
1212 get_storage_drive(ua, store.store);
1214 max_slots = get_num_slots_from_SD(ua);
1216 if (max_slots <= 0) {
1217 ua->warning_msg(_("No slots in changer to scan.\n"));
1220 slot_list = (char *)malloc(max_slots+1);
1221 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1226 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1229 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1232 ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
1233 ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1235 /* Walk through the list getting the media records */
1236 for (vl=vol_list; vl; vl=vl->next) {
1237 if (vl->Slot > max_slots) {
1238 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1239 vl->Slot, max_slots);
1242 /* Check if user wants us to look at this slot */
1243 if (!slot_list[vl->Slot]) {
1244 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1248 slot_list[vl->Slot] = 0; /* clear Slot */
1251 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1252 ua->send_msg(slot_hformat,
1254 "?", "?", "?", "?");
1258 /* Hope that slots are ordered */
1259 for (; i < vl->Slot; i++) {
1261 ua->send_msg(slot_hformat,
1262 i, ' ', "", "", "", "");
1267 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1269 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1270 memset(&pr, 0, sizeof(POOL_DBR));
1271 pr.PoolId = mr.PoolId;
1272 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1273 strcpy(pr.Name, "?");
1277 /* Print information */
1278 ua->send_msg(slot_hformat,
1279 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1280 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1282 } else { /* TODO: get information from catalog */
1283 ua->send_msg(slot_hformat,
1285 mr.VolumeName, "?", "?", "?");
1290 /* Display the rest of the autochanger
1292 for (; i <= max_slots; i++) {
1294 ua->send_msg(slot_hformat,
1295 i, ' ', "", "", "", "");
1302 free_vol_list(vol_list);