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 if (debug_level >= 100) {
151 Dmsg0(100, "Slots turned on:\n");
152 for (i=1; i <= num_slots; i++) {
154 Dmsg1(100, "%d\n", i);
161 Dmsg1(100, "Problem with user selection ERR=%s\n", msg);
166 * Update Slots corresponding to Volumes in autochanger
168 void update_slots(UAContext *ua)
171 vol_list_t *vl, *vol_list = NULL;
182 if (!open_client_db(ua)) {
185 store.store = get_storage_resource(ua, true/*arg is storage*/);
189 pm_strcpy(store.store_source, _("command line"));
190 set_wstorage(ua->jcr, &store);
191 drive = get_storage_drive(ua, store.store);
193 scan = find_arg(ua, NT_("scan")) >= 0;
194 if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
195 Enabled = get_enabled(ua, ua->argv[i]);
201 have_enabled = false;
204 max_slots = get_num_slots_from_SD(ua);
205 Dmsg1(100, "max_slots=%d\n", max_slots);
206 if (max_slots <= 0) {
207 ua->warning_msg(_("No slots in changer to scan.\n"));
210 slot_list = (char *)malloc(max_slots+1);
211 if (!get_user_slot_list(ua, slot_list, max_slots)) {
216 vol_list = get_vol_list_from_SD(ua, scan);
219 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
223 /* First zap out any InChanger with StorageId=0 */
224 db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);
226 /* Walk through the list updating the media records */
227 for (vl=vol_list; vl; vl=vl->next) {
228 if (vl->Slot > max_slots) {
229 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
230 vl->Slot, max_slots);
233 /* Check if user wants us to look at this slot */
234 if (!slot_list[vl->Slot]) {
235 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
238 /* If scanning, we read the label rather than the barcode */
244 vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
245 Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
247 slot_list[vl->Slot] = 0; /* clear Slot */
250 mr.MediaId = 0; /* Force using VolumeName */
252 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
254 mr.VolumeName[0] = 0;
256 set_storageid_in_mr(store.store, &mr);
257 Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
258 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
260 /* Set InChanger to zero for this Slot */
261 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
263 Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
264 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
266 Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
267 ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
271 Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
272 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
273 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
274 Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
275 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
276 /* If Slot, Inchanger, and StorageId have changed, update the Media record */
277 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
281 mr.Enabled = Enabled;
283 set_storageid_in_mr(store.store, &mr);
284 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
285 ua->error_msg("%s", db_strerror(ua->db));
288 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
289 mr.VolumeName, mr.Slot);
292 ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
298 ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
299 mr.VolumeName, vl->Slot);
305 set_storageid_in_mr(store.store, &mr);
307 for (int i=1; i <= max_slots; i++) {
310 /* Set InChanger to zero for this Slot */
311 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
318 free_vol_list(vol_list);
327 * Common routine for both label and relabel
329 static int do_label(UAContext *ua, const char *cmd, int relabel)
333 char dev_name[MAX_NAME_LENGTH];
336 bool print_reminder = true;
337 bool label_barcodes = false;
341 bool media_record_exists = false;
342 static const char *barcode_keyword[] = {
348 memset(&pr, 0, sizeof(pr));
349 if (!open_client_db(ua)) {
353 /* Look for one of the barcode keywords */
354 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
355 /* Now find the keyword in the list */
356 if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
357 *ua->argk[j] = 0; /* zap barcode keyword */
359 label_barcodes = true;
362 store.store = get_storage_resource(ua, true/*use default*/);
366 pm_strcpy(store.store_source, _("command line"));
367 set_wstorage(ua->jcr, &store);
368 drive = get_storage_drive(ua, store.store);
370 if (label_barcodes) {
371 label_from_barcodes(ua, drive);
375 /* If relabel get name of Volume to relabel */
377 /* Check for oldvolume=name */
378 i = find_arg_with_value(ua, "oldvolume");
380 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
381 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
384 ua->error_msg("%s", db_strerror(ua->db));
386 /* No keyword or Vol not found, ask user to select */
387 if (!select_media_dbr(ua, &omr)) {
391 /* Require Volume to be Purged or Recycled */
393 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
394 ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
395 omr.VolumeName, omr.VolStatus);
400 /* Check for volume=NewVolume */
401 i = find_arg_with_value(ua, "volume");
403 pm_strcpy(ua->cmd, ua->argv[i]);
407 /* Get a new Volume name */
409 media_record_exists = false;
410 if (!get_cmd(ua, _("Enter new Volume name: "))) {
414 if (!is_volume_name_legal(ua, ua->cmd)) {
418 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
419 /* If VolBytes are zero the Volume is not labeled */
420 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
421 if (mr.VolBytes != 0) {
422 ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
426 media_record_exists = true;
431 /* If autochanger, request slot */
432 i = find_arg_with_value(ua, "slot");
434 mr.Slot = atoi(ua->argv[i]);
438 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
439 } else if (store.store->autochanger) {
440 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
443 mr.Slot = ua->pint32_val;
447 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
449 set_storageid_in_mr(store.store, &mr);
451 bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
453 /* Must select Pool if not already done */
454 if (pr.PoolId == 0) {
455 memset(&pr, 0, sizeof(pr));
456 if (!select_pool_dbr(ua, &pr)) {
461 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
464 sd = ua->jcr->store_bsock;
466 /* Delete the old media record */
467 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
468 ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
469 omr.VolumeName, db_strerror(ua->db));
471 ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"),
473 /* Update the number of Volumes in the pool */
475 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
476 ua->error_msg("%s", db_strerror(ua->db));
481 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
482 ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
483 bash_spaces(dev_name);
484 bnet_fsend(sd, "mount %s drive=%d", dev_name, drive);
485 unbash_spaces(dev_name);
486 while (bnet_recv(sd) >= 0) {
487 ua->send_msg("%s", sd->msg);
489 * 3001 OK mount. Device=xxx or
490 * 3001 Mounted Volume vvvv
491 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
492 * 3906 is cannot mount non-tape
493 * So for those, no need to print a reminder
495 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
496 strncmp(sd->msg, "3002 ", 5) == 0 ||
497 strncmp(sd->msg, "3906 ", 5) == 0) {
498 print_reminder = false;
503 if (print_reminder) {
504 ua->info_msg(_("Do not forget to mount the drive!!!\n"));
512 * Request SD to send us the slot:barcodes, then wiffle
513 * through them all labeling them.
515 static void label_from_barcodes(UAContext *ua, int drive)
517 STORE *store = ua->jcr->wstore;
520 vol_list_t *vl, *vol_list = NULL;
521 bool media_record_exists;
526 max_slots = get_num_slots_from_SD(ua);
527 if (max_slots <= 0) {
528 ua->warning_msg(_("No slots in changer to scan.\n"));
531 slot_list = (char *)malloc(max_slots+1);
532 if (!get_user_slot_list(ua, slot_list, max_slots)) {
536 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
539 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
543 /* Display list of Volumes and ask if he really wants to proceed */
544 ua->send_msg(_("The following Volumes will be labeled:\n"
546 "==============\n"));
547 for (vl=vol_list; vl; vl=vl->next) {
548 if (!vl->VolName || !slot_list[vl->Slot]) {
551 ua->send_msg("%4d %s\n", vl->Slot, vl->VolName);
553 if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
554 (ua->pint32_val == 0)) {
558 memset(&pr, 0, sizeof(pr));
559 if (!select_pool_dbr(ua, &pr)) {
563 /* Fire off the label requests */
564 for (vl=vol_list; vl; vl=vl->next) {
565 if (!vl->VolName || !slot_list[vl->Slot]) {
569 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
570 media_record_exists = false;
571 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
572 if (mr.VolBytes != 0) {
573 ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
574 vl->Slot, mr.VolumeName);
576 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
577 set_storageid_in_mr(store, &mr);
578 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
579 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
583 media_record_exists = true;
585 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
586 set_storageid_in_mr(store, &mr);
588 * Deal with creating cleaning tape here. Normal tapes created in
589 * send_label_request() below
591 if (is_cleaning_tape(ua, &mr, &pr)) {
592 if (media_record_exists) { /* we update it */
593 mr.VolBytes = 1; /* any bytes to indicate it exists */
594 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
596 set_storageid_in_mr(store, &mr);
597 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
598 ua->error_msg("%s", db_strerror(ua->db));
600 } else { /* create the media record */
601 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
602 ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
605 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
606 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
608 set_storageid_in_mr(store, &mr);
609 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
610 ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
612 pr.NumVols++; /* this is a bit suspect */
613 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
614 ua->error_msg("%s", db_strerror(ua->db));
617 ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
620 continue; /* done, go handle next volume */
622 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
625 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
631 free_vol_list(vol_list);
638 * Check if the Volume name has legal characters
639 * If ua is non-NULL send the message
641 bool is_volume_name_legal(UAContext *ua, const char *name)
645 const char *accept = ":.-_";
647 /* Restrict the characters permitted in the Volume name */
648 for (p=name; *p; p++) {
649 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
653 ua->error_msg(_("Illegal character \"%c\" in a volume name.\n"), *p);
658 if (len >= MAX_NAME_LENGTH) {
660 ua->error_msg(_("Volume name too long.\n"));
666 ua->error_msg(_("Volume name must be at least one character long.\n"));
674 * NOTE! This routine opens the SD socket but leaves it open
676 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
677 POOL_DBR *pr, int relabel, bool media_record_exists,
681 char dev_name[MAX_NAME_LENGTH];
684 uint64_t VolBytes = 0;
686 if (!(sd=open_sd_bsock(ua))) {
689 bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
690 bash_spaces(dev_name);
691 bash_spaces(mr->VolumeName);
692 bash_spaces(mr->MediaType);
693 bash_spaces(pr->Name);
695 bash_spaces(omr->VolumeName);
696 sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
697 "MediaType=%s Slot=%d drive=%d",
698 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
699 mr->MediaType, mr->Slot, drive);
700 ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
701 omr->VolumeName, mr->VolumeName);
703 sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
705 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
707 ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"),
708 mr->VolumeName, mr->Slot);
709 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
710 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
713 while (sd->recv() >= 0) {
715 ua->send_msg("%s", sd->msg);
716 if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes,
722 unbash_spaces(mr->VolumeName);
723 unbash_spaces(mr->MediaType);
724 unbash_spaces(pr->Name);
725 mr->LabelDate = time(NULL);
726 mr->set_label_date = true;
728 /* We know that a freshly labelled DVD has 1 VolParts */
729 /* This does not apply to auto-labelled DVDs. */
733 if (media_record_exists) { /* we update it */
734 mr->VolBytes = VolBytes;
735 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
736 set_storageid_in_mr(ua->jcr->wstore, mr);
737 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
738 ua->error_msg("%s", db_strerror(ua->db));
741 } else { /* create the media record */
742 set_pool_dbr_defaults_in_media_dbr(mr, pr);
743 mr->VolBytes = VolBytes;
744 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
746 set_storageid_in_mr(ua->jcr->wstore, mr);
747 if (db_create_media_record(ua->jcr, ua->db, mr)) {
748 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
749 mr->VolumeName, mr->Slot);
750 /* Update number of volumes in pool */
752 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
753 ua->error_msg("%s", db_strerror(ua->db));
756 ua->error_msg("%s", db_strerror(ua->db));
761 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
766 static BSOCK *open_sd_bsock(UAContext *ua)
768 STORE *store = ua->jcr->wstore;
770 if (!ua->jcr->store_bsock) {
771 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d ...\n"),
772 store->name(), store->address, store->SDport);
773 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
774 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
778 return ua->jcr->store_bsock;
781 static void close_sd_bsock(UAContext *ua)
783 if (ua->jcr->store_bsock) {
784 bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
785 bnet_close(ua->jcr->store_bsock);
786 ua->jcr->store_bsock = NULL;
790 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
792 STORE *store = ua->jcr->wstore;
794 char dev_name[MAX_NAME_LENGTH];
795 char *VolName = NULL;
798 if (!(sd=open_sd_bsock(ua))) {
799 ua->error_msg(_("Could not open SD socket.\n"));
802 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
803 bash_spaces(dev_name);
804 /* Ask for autochanger list of volumes */
805 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
806 Dmsg1(100, "Sent: %s", sd->msg);
808 /* Get Volume name in this Slot */
809 while (sd->recv() >= 0) {
810 ua->send_msg("%s", sd->msg);
811 Dmsg1(100, "Got: %s", sd->msg);
812 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
813 VolName = (char *)malloc(sd->msglen);
814 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
822 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
827 * We get the slot list from the Storage daemon.
828 * If scan is set, we return all slots found,
829 * otherwise, we return only slots with valid barcodes (Volume names)
831 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
833 STORE *store = ua->jcr->wstore;
834 char dev_name[MAX_NAME_LENGTH];
837 vol_list_t *vol_list = NULL;
840 if (!(sd=open_sd_bsock(ua))) {
844 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
845 bash_spaces(dev_name);
846 /* Ask for autochanger list of volumes */
847 bnet_fsend(sd, NT_("autochanger list %s \n"), dev_name);
849 /* Read and organize list of Volumes */
850 while (bnet_recv(sd) >= 0) {
853 strip_trailing_junk(sd->msg);
855 /* Check for returned SD messages */
856 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
857 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
859 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
863 /* Validate Slot: if scanning, otherwise Slot:Barcode */
864 p = strchr(sd->msg, ':');
866 /* Scanning -- require only valid slot */
867 Slot = atoi(sd->msg);
871 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
876 if (p && strlen(p) > 1) {
878 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
881 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
887 if (!is_volume_name_legal(ua, p)) {
890 ua->error_msg(_("Invalid Volume name: %s\n"), sd->msg);
895 /* Add Slot and VolumeName to list */
896 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
900 p++; /* skip separator */
902 vl->VolName = bstrdup(p);
906 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
911 vol_list_t *prev=vol_list;
912 /* Add new entry to the right place in the list */
913 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
914 if (tvl->Slot > vl->Slot) {
915 /* no previous item, update vol_list directly */
916 if (prev == vol_list) {
920 } else { /* replace the previous pointer */
926 /* we are at the end */
940 static void free_vol_list(vol_list_t *vol_list)
945 for (vl=vol_list; vl; ) {
957 * We get the number of slots in the changer from the SD
959 static int get_num_slots_from_SD(UAContext *ua)
961 STORE *store = ua->jcr->wstore;
962 char dev_name[MAX_NAME_LENGTH];
967 if (!(sd=open_sd_bsock(ua))) {
971 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
972 bash_spaces(dev_name);
973 /* Ask for autochanger number of slots */
974 sd->fsend(NT_("autochanger slots %s\n"), dev_name);
976 while (sd->recv() >= 0) {
977 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
980 ua->send_msg("%s", sd->msg);
984 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
989 * We get the number of drives in the changer from the SD
991 int get_num_drives_from_SD(UAContext *ua)
993 STORE *store = ua->jcr->wstore;
994 char dev_name[MAX_NAME_LENGTH];
999 if (!(sd=open_sd_bsock(ua))) {
1003 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1004 bash_spaces(dev_name);
1005 /* Ask for autochanger number of slots */
1006 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
1008 while (sd->recv() >= 0) {
1009 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
1012 ua->send_msg("%s", sd->msg);
1016 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
1021 * Check if this is a cleaning tape by comparing the Volume name
1022 * with the Cleaning Prefix. If they match, this is a cleaning
1025 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
1027 /* Find Pool resource */
1028 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
1029 if (!ua->jcr->pool) {
1030 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
1031 pr->Name, mr->VolumeName);
1034 if (ua->jcr->pool->cleaning_prefix == NULL) {
1037 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1038 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1039 strlen(ua->jcr->pool->cleaning_prefix),
1040 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1041 strlen(ua->jcr->pool->cleaning_prefix)));
1042 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1043 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1046 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1048 char ed1[50], ed2[50], ed3[50];
1051 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1052 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1053 const char *slot_api_empty_format="%c|%i||||||||\n";
1055 if (is_volume_name_legal(NULL, vol_name)) {
1056 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1057 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1058 memset(&pr, 0, sizeof(POOL_DBR));
1059 pr.PoolId = mr.PoolId;
1060 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1061 strcpy(pr.Name, "?");
1063 ua->send_msg(slot_api_full_format, type,
1064 Slot, mr.Slot, mr.VolumeName,
1065 edit_uint64(mr.VolBytes, ed1),
1066 mr.VolStatus, mr.MediaType, pr.Name,
1067 edit_uint64(mr.LastWritten, ed2),
1068 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1070 } else { /* Media unknown */
1071 ua->send_msg(slot_api_full_format,
1072 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1077 ua->send_msg(slot_api_empty_format, type, Slot);
1082 * Input (output of mxt-changer listall):
1084 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1085 * D:0:F:2:vol2 or D:Drive num:E
1090 * S:1:F:vol1 S:Slot num:F:Volume Name
1091 * S:2:E or S:Slot num:E
1094 * Import/Export tray slots:
1095 * I:10:F:vol10 I:Slot num:F:Volume Name
1096 * I:11:E or I:Slot num:E
1099 * If a drive is loaded, the slot *should* be empty
1103 * Drive list: D|Drive num|Slot loaded|Volume Name
1108 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1110 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1112 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1114 * TODO: need to merge with status_slots()
1116 void status_content(UAContext *ua, STORE *store)
1120 char dev_name[MAX_NAME_LENGTH];
1121 char vol_name[MAX_NAME_LENGTH];
1123 vol_list_t *vl=NULL, *vol_list = NULL;
1125 if (!(sd=open_sd_bsock(ua))) {
1129 if (!open_client_db(ua)) {
1133 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1134 bash_spaces(dev_name);
1135 /* Ask for autochanger list of volumes */
1136 bnet_fsend(sd, NT_("autochanger listall %s \n"), dev_name);
1138 /* Read and organize list of Drive, Slots and I/O Slots */
1139 while (bnet_recv(sd) >= 0) {
1140 strip_trailing_junk(sd->msg);
1142 /* Check for returned SD messages */
1143 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1144 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1145 sd->msg[4] == ' ') {
1146 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1153 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1154 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1156 /* we print information on the slot if we have a volume name */
1158 /* Add Slot and VolumeName to list */
1159 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1161 vl->VolName = bstrdup(vol_name);
1162 vl->next = vol_list;
1166 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1167 ua->send_msg("D|%d||\n", Drive);
1169 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1170 content_send_info(ua, type, Slot, vol_name);
1172 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1173 /* type can be S (slot) or I (Import/Export slot) */
1174 vol_list_t *prev=NULL;
1175 for (vl = vol_list; vl; vl = vl->next) {
1176 if (vl->Slot == Slot) {
1177 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1179 /* remove the node */
1181 prev->next = vl->next;
1183 vol_list = vl->next;
1191 content_send_info(ua, type, Slot, vol_name);
1194 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1201 * Print slots from AutoChanger
1203 void status_slots(UAContext *ua, STORE *store_r)
1207 vol_list_t *vl, *vol_list = NULL;
1212 /* Slot | Volume | Status | MediaType | Pool */
1213 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1216 status_content(ua, store_r);
1220 if (!open_client_db(ua)) {
1223 store.store = store_r;
1225 pm_strcpy(store.store_source, _("command line"));
1226 set_wstorage(ua->jcr, &store);
1227 get_storage_drive(ua, store.store);
1229 max_slots = get_num_slots_from_SD(ua);
1231 if (max_slots <= 0) {
1232 ua->warning_msg(_("No slots in changer to scan.\n"));
1235 slot_list = (char *)malloc(max_slots+1);
1236 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1241 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1244 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1247 ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
1248 ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1250 /* Walk through the list getting the media records */
1251 for (vl=vol_list; vl; vl=vl->next) {
1252 if (vl->Slot > max_slots) {
1253 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1254 vl->Slot, max_slots);
1257 /* Check if user wants us to look at this slot */
1258 if (!slot_list[vl->Slot]) {
1259 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1263 slot_list[vl->Slot] = 0; /* clear Slot */
1266 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1267 ua->send_msg(slot_hformat,
1269 "?", "?", "?", "?");
1273 /* Hope that slots are ordered */
1274 for (; i < vl->Slot; i++) {
1276 ua->send_msg(slot_hformat,
1277 i, ' ', "", "", "", "");
1282 memset(&mr, 0, sizeof(MEDIA_DBR));
1283 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1285 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1286 memset(&pr, 0, sizeof(POOL_DBR));
1287 pr.PoolId = mr.PoolId;
1288 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1289 strcpy(pr.Name, "?");
1292 /* Print information */
1293 ua->send_msg(slot_hformat,
1294 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1295 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1297 } else { /* TODO: get information from catalog */
1298 ua->send_msg(slot_hformat,
1300 mr.VolumeName, "?", "?", "?");
1304 /* Display the rest of the autochanger
1306 for (; i <= max_slots; i++) {
1308 ua->send_msg(slot_hformat,
1309 i, ' ', "", "", "", "");
1316 free_vol_list(vol_list);