2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2008 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 two of the GNU 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 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
40 /* Slot list definition */
41 typedef struct s_vol_list {
42 struct s_vol_list *next;
48 /* Forward referenced functions */
49 static int do_label(UAContext *ua, const char *cmd, int relabel);
50 static void label_from_barcodes(UAContext *ua, int drive);
51 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
52 POOL_DBR *pr, int relabel, bool media_record_exits, int drive);
53 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan);
54 static void free_vol_list(vol_list_t *vol_list);
55 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr);
56 static BSOCK *open_sd_bsock(UAContext *ua);
57 static void close_sd_bsock(UAContext *ua);
58 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive);
59 static int get_num_slots_from_SD(UAContext *ua);
65 * label storage=xxx volume=vvv
67 int label_cmd(UAContext *ua, const char *cmd)
69 return do_label(ua, cmd, 0); /* standard label */
72 int relabel_cmd(UAContext *ua, const char *cmd)
74 return do_label(ua, cmd, 1); /* relabel tape */
77 static bool get_user_slot_list(UAContext *ua, char *slot_list, int num_slots)
82 /* slots are numbered 1 to num_slots */
83 for (int i=0; i <= num_slots; i++) {
86 i = find_arg_with_value(ua, "slots");
88 /* scan slot list in ua->argv[i] */
92 strip_trailing_junk(ua->argv[i]);
93 for (p=ua->argv[i]; p && *p; p=e) {
100 h = strchr(p, '-'); /* range? */
102 msg = _("Negative numbers not permitted\n");
107 if (!is_an_integer(h)) {
108 msg = _("Range end is not integer.\n");
112 if (!is_an_integer(p)) {
113 msg = _("Range start is not an integer.\n");
119 msg = _("Range end not bigger than start.\n");
124 if (!is_an_integer(p)) {
125 msg = _("Input value is not an integer.\n");
130 if (beg <= 0 || end <= 0) {
131 msg = _("Values must be be greater than zero.\n");
134 if (end > num_slots) {
135 msg = _("Slot too large.\n");
138 for (i=beg; i<=end; i++) {
139 slot_list[i] = 1; /* Turn on specified range */
143 /* Turn everything on */
144 for (i=1; i <= num_slots; i++) {
148 Dmsg0(100, "Slots turned on:\n");
149 for (i=1; i <= num_slots; i++) {
151 Dmsg1(100, "%d\n", i);
161 * Update Slots corresponding to Volumes in autochanger
163 void update_slots(UAContext *ua)
166 vol_list_t *vl, *vol_list = NULL;
177 if (!open_client_db(ua)) {
180 store.store = get_storage_resource(ua, true/*arg is storage*/);
184 pm_strcpy(store.store_source, _("command line"));
185 set_wstorage(ua->jcr, &store);
186 drive = get_storage_drive(ua, store.store);
188 scan = find_arg(ua, NT_("scan")) >= 0;
189 if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
190 Enabled = get_enabled(ua, ua->argv[i]);
196 have_enabled = false;
199 max_slots = get_num_slots_from_SD(ua);
200 Dmsg1(100, "max_slots=%d\n", max_slots);
201 if (max_slots <= 0) {
202 ua->warning_msg(_("No slots in changer to scan.\n"));
205 slot_list = (char *)malloc(max_slots+1);
206 if (!get_user_slot_list(ua, slot_list, max_slots)) {
211 vol_list = get_vol_list_from_SD(ua, scan);
214 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
218 /* First zap out any InChanger with StorageId=0 */
219 db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);
221 /* Walk through the list updating the media records */
222 for (vl=vol_list; vl; vl=vl->next) {
223 if (vl->Slot > max_slots) {
224 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
225 vl->Slot, max_slots);
228 /* Check if user wants us to look at this slot */
229 if (!slot_list[vl->Slot]) {
230 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
233 /* If scanning, we read the label rather than the barcode */
239 vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
240 Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
242 slot_list[vl->Slot] = 0; /* clear Slot */
243 memset(&mr, 0, sizeof(mr));
246 mr.StorageId = store.store->StorageId;
247 /* Set InChanger to zero for this Slot */
249 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
252 Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
253 ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
256 memset(&mr, 0, sizeof(mr));
257 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
259 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
260 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
263 mr.StorageId = store.store->StorageId;
265 mr.Enabled = Enabled;
267 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
268 ua->error_msg("%s", db_strerror(ua->db));
271 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
272 mr.VolumeName, mr.Slot);
275 ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
281 ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
282 mr.VolumeName, vl->Slot);
286 memset(&mr, 0, sizeof(mr));
288 mr.StorageId = store.store->StorageId;
290 for (int i=1; i <= max_slots; i++) {
293 /* Set InChanger to zero for this Slot */
294 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
301 free_vol_list(vol_list);
310 * Common routine for both label and relabel
312 static int do_label(UAContext *ua, const char *cmd, int relabel)
316 char dev_name[MAX_NAME_LENGTH];
319 bool print_reminder = true;
320 bool label_barcodes = false;
324 bool media_record_exists = false;
325 static const char *barcode_keyword[] = {
331 memset(&pr, 0, sizeof(pr));
332 if (!open_client_db(ua)) {
336 /* Look for one of the barcode keywords */
337 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
338 /* Now find the keyword in the list */
339 if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
340 *ua->argk[j] = 0; /* zap barcode keyword */
342 label_barcodes = true;
345 store.store = get_storage_resource(ua, true/*use default*/);
349 pm_strcpy(store.store_source, _("command line"));
350 set_wstorage(ua->jcr, &store);
351 drive = get_storage_drive(ua, store.store);
353 if (label_barcodes) {
354 label_from_barcodes(ua, drive);
358 /* If relabel get name of Volume to relabel */
360 /* Check for oldvolume=name */
361 i = find_arg_with_value(ua, "oldvolume");
363 memset(&omr, 0, sizeof(omr));
364 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
365 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
368 ua->error_msg("%s", db_strerror(ua->db));
370 /* No keyword or Vol not found, ask user to select */
371 if (!select_media_dbr(ua, &omr)) {
375 /* Require Volume to be Purged or Recycled */
377 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
378 ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
379 omr.VolumeName, omr.VolStatus);
384 /* Check for volume=NewVolume */
385 i = find_arg_with_value(ua, "volume");
387 pm_strcpy(ua->cmd, ua->argv[i]);
391 /* Get a new Volume name */
393 media_record_exists = false;
394 if (!get_cmd(ua, _("Enter new Volume name: "))) {
398 if (!is_volume_name_legal(ua, ua->cmd)) {
402 memset(&mr, 0, sizeof(mr));
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 mr.StorageId = store.store->StorageId;
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)) {
547 memset(&omr, 0, sizeof(omr));
549 /* Fire off the label requests */
550 for (vl=vol_list; vl; vl=vl->next) {
551 if (!vl->VolName || !slot_list[vl->Slot]) {
554 memset(&mr, 0, sizeof(mr));
555 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
556 media_record_exists = false;
557 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
558 if (mr.VolBytes != 0) {
559 ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
560 vl->Slot, mr.VolumeName);
562 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
563 mr.StorageId = store->StorageId;
564 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
565 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
569 media_record_exists = true;
571 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
572 mr.StorageId = store->StorageId;
574 * Deal with creating cleaning tape here. Normal tapes created in
575 * send_label_request() below
577 if (is_cleaning_tape(ua, &mr, &pr)) {
578 if (media_record_exists) { /* we update it */
579 mr.VolBytes = 1; /* any bytes to indicate it exists */
580 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
582 mr.StorageId = store->StorageId;
583 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
584 ua->error_msg("%s", db_strerror(ua->db));
586 } else { /* create the media record */
587 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
588 ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
591 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
592 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
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 mr->StorageId = ua->jcr->wstore->StorageId;
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 */
730 mr->StorageId = ua->jcr->wstore->StorageId;
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 memset(&mr, 0, sizeof(mr));
1042 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1043 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1044 memset(&pr, 0, sizeof(POOL_DBR));
1045 pr.PoolId = mr.PoolId;
1046 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1047 strcpy(pr.Name, "?");
1049 ua->send_msg(slot_api_full_format, type,
1050 Slot, mr.Slot, mr.VolumeName,
1051 edit_uint64(mr.VolBytes, ed1),
1052 mr.VolStatus, mr.MediaType, pr.Name,
1053 edit_uint64(mr.LastWritten, ed2),
1054 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1056 } else { /* Media unknown */
1057 ua->send_msg(slot_api_full_format,
1058 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1063 ua->send_msg(slot_api_empty_format, type, Slot);
1068 * Input (output of mxt-changer listall):
1070 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1071 * D:0:F:2:vol2 or D:Drive num:E
1076 * S:1:F:vol1 S:Slot num:F:Volume Name
1077 * S:2:E or S:Slot num:E
1080 * Import/Export tray slots:
1081 * I:10:F:vol10 I:Slot num:F:Volume Name
1082 * I:11:E or I:Slot num:E
1085 * If a drive is loaded, the slot *should* be empty
1089 * Drive list: D|Drive num|Slot loaded|Volume Name
1094 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1096 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1098 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1100 * TODO: need to merge with status_slots()
1102 void status_content(UAContext *ua, STORE *store)
1106 char dev_name[MAX_NAME_LENGTH];
1107 char vol_name[MAX_NAME_LENGTH];
1109 vol_list_t *vl=NULL, *vol_list = NULL;
1111 if (!(sd=open_sd_bsock(ua))) {
1115 if (!open_client_db(ua)) {
1119 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1120 bash_spaces(dev_name);
1121 /* Ask for autochanger list of volumes */
1122 bnet_fsend(sd, NT_("autochanger listall %s \n"), dev_name);
1124 /* Read and organize list of Drive, Slots and I/O Slots */
1125 while (bnet_recv(sd) >= 0) {
1126 strip_trailing_junk(sd->msg);
1128 /* Check for returned SD messages */
1129 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1130 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1131 sd->msg[4] == ' ') {
1132 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1139 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1140 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1142 /* we print information on the slot if we have a volume name */
1144 /* Add Slot and VolumeName to list */
1145 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1147 vl->VolName = bstrdup(vol_name);
1148 vl->next = vol_list;
1152 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1153 ua->send_msg("D|%d||\n", Drive);
1155 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1156 content_send_info(ua, type, Slot, vol_name);
1158 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1159 /* type can be S (slot) or I (Import/Export slot) */
1160 vol_list_t *prev=NULL;
1161 for (vl = vol_list; vl; vl = vl->next) {
1162 if (vl->Slot == Slot) {
1163 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1165 /* remove the node */
1167 prev->next = vl->next;
1169 vol_list = vl->next;
1177 content_send_info(ua, type, Slot, vol_name);
1180 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1187 * Print slots from AutoChanger
1189 void status_slots(UAContext *ua, STORE *store_r)
1193 vol_list_t *vl, *vol_list = NULL;
1199 /* Slot | Volume | Status | MediaType | Pool */
1200 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1203 status_content(ua, store_r);
1207 if (!open_client_db(ua)) {
1210 store.store = store_r;
1212 pm_strcpy(store.store_source, _("command line"));
1213 set_wstorage(ua->jcr, &store);
1214 drive = get_storage_drive(ua, store.store);
1216 max_slots = get_num_slots_from_SD(ua);
1218 if (max_slots <= 0) {
1219 ua->warning_msg(_("No slots in changer to scan.\n"));
1222 slot_list = (char *)malloc(max_slots+1);
1223 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1228 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1231 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1234 ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
1235 ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1237 /* Walk through the list getting the media records */
1238 for (vl=vol_list; vl; vl=vl->next) {
1239 if (vl->Slot > max_slots) {
1240 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1241 vl->Slot, max_slots);
1244 /* Check if user wants us to look at this slot */
1245 if (!slot_list[vl->Slot]) {
1246 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1250 slot_list[vl->Slot] = 0; /* clear Slot */
1253 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1254 ua->send_msg(slot_hformat,
1256 "?", "?", "?", "?");
1260 /* Hope that slots are ordered */
1261 for (; i < vl->Slot; i++) {
1263 ua->send_msg(slot_hformat,
1264 i, ' ', "", "", "", "");
1269 memset(&mr, 0, sizeof(mr));
1270 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1272 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1273 memset(&pr, 0, sizeof(POOL_DBR));
1274 pr.PoolId = mr.PoolId;
1275 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1276 strcpy(pr.Name, "?");
1280 /* Print information */
1281 ua->send_msg(slot_hformat,
1282 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1283 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1285 } else { /* TODO: get information from catalog */
1286 ua->send_msg(slot_hformat,
1288 mr.VolumeName, "?", "?", "?");
1293 /* Display the rest of the autochanger
1295 for (; i <= max_slots; i++) {
1297 ua->send_msg(slot_hformat,
1298 i, ' ', "", "", "", "");
1305 free_vol_list(vol_list);