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 mr.MediaId = 0; /* Force using VolumeName */
250 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
252 mr.VolumeName[0] = 0;
254 set_storageid_in_mr(store.store, &mr);
255 Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
256 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
258 /* Set InChanger to zero for this Slot */
259 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
261 Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
262 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
264 Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
265 ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
269 Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
270 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
271 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
272 Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
273 mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
274 /* If Slot, Inchanger, and StorageId have changed, update the Media record */
275 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
279 mr.Enabled = Enabled;
281 set_storageid_in_mr(store.store, &mr);
282 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
283 ua->error_msg("%s", db_strerror(ua->db));
286 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
287 mr.VolumeName, mr.Slot);
290 ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
296 ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
297 mr.VolumeName, vl->Slot);
303 set_storageid_in_mr(store.store, &mr);
305 for (int i=1; i <= max_slots; i++) {
308 /* Set InChanger to zero for this Slot */
309 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
316 free_vol_list(vol_list);
325 * Common routine for both label and relabel
327 static int do_label(UAContext *ua, const char *cmd, int relabel)
331 char dev_name[MAX_NAME_LENGTH];
334 bool print_reminder = true;
335 bool label_barcodes = false;
339 bool media_record_exists = false;
340 static const char *barcode_keyword[] = {
346 memset(&pr, 0, sizeof(pr));
347 if (!open_client_db(ua)) {
351 /* Look for one of the barcode keywords */
352 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
353 /* Now find the keyword in the list */
354 if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
355 *ua->argk[j] = 0; /* zap barcode keyword */
357 label_barcodes = true;
360 store.store = get_storage_resource(ua, true/*use default*/);
364 pm_strcpy(store.store_source, _("command line"));
365 set_wstorage(ua->jcr, &store);
366 drive = get_storage_drive(ua, store.store);
368 if (label_barcodes) {
369 label_from_barcodes(ua, drive);
373 /* If relabel get name of Volume to relabel */
375 /* Check for oldvolume=name */
376 i = find_arg_with_value(ua, "oldvolume");
378 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
379 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
382 ua->error_msg("%s", db_strerror(ua->db));
384 /* No keyword or Vol not found, ask user to select */
385 if (!select_media_dbr(ua, &omr)) {
389 /* Require Volume to be Purged or Recycled */
391 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
392 ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
393 omr.VolumeName, omr.VolStatus);
398 /* Check for volume=NewVolume */
399 i = find_arg_with_value(ua, "volume");
401 pm_strcpy(ua->cmd, ua->argv[i]);
405 /* Get a new Volume name */
407 media_record_exists = false;
408 if (!get_cmd(ua, _("Enter new Volume name: "))) {
412 if (!is_volume_name_legal(ua, ua->cmd)) {
416 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
417 /* If VolBytes are zero the Volume is not labeled */
418 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
419 if (mr.VolBytes != 0) {
420 ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
424 media_record_exists = true;
429 /* If autochanger, request slot */
430 i = find_arg_with_value(ua, "slot");
432 mr.Slot = atoi(ua->argv[i]);
436 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
437 } else if (store.store->autochanger) {
438 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
441 mr.Slot = ua->pint32_val;
445 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
447 set_storageid_in_mr(store.store, &mr);
449 bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
451 /* Must select Pool if not already done */
452 if (pr.PoolId == 0) {
453 memset(&pr, 0, sizeof(pr));
454 if (!select_pool_dbr(ua, &pr)) {
459 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
462 sd = ua->jcr->store_bsock;
464 /* Delete the old media record */
465 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
466 ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
467 omr.VolumeName, db_strerror(ua->db));
469 ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"),
471 /* Update the number of Volumes in the pool */
473 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
474 ua->error_msg("%s", db_strerror(ua->db));
479 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
480 ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
481 bash_spaces(dev_name);
482 bnet_fsend(sd, "mount %s drive=%d", dev_name, drive);
483 unbash_spaces(dev_name);
484 while (bnet_recv(sd) >= 0) {
485 ua->send_msg("%s", sd->msg);
487 * 3001 OK mount. Device=xxx or
488 * 3001 Mounted Volume vvvv
489 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
490 * 3906 is cannot mount non-tape
491 * So for those, no need to print a reminder
493 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
494 strncmp(sd->msg, "3002 ", 5) == 0 ||
495 strncmp(sd->msg, "3906 ", 5) == 0) {
496 print_reminder = false;
501 if (print_reminder) {
502 ua->info_msg(_("Do not forget to mount the drive!!!\n"));
510 * Request SD to send us the slot:barcodes, then wiffle
511 * through them all labeling them.
513 static void label_from_barcodes(UAContext *ua, int drive)
515 STORE *store = ua->jcr->wstore;
518 vol_list_t *vl, *vol_list = NULL;
519 bool media_record_exists;
524 max_slots = get_num_slots_from_SD(ua);
525 if (max_slots <= 0) {
526 ua->warning_msg(_("No slots in changer to scan.\n"));
529 slot_list = (char *)malloc(max_slots+1);
530 if (!get_user_slot_list(ua, slot_list, max_slots)) {
534 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
537 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
541 /* Display list of Volumes and ask if he really wants to proceed */
542 ua->send_msg(_("The following Volumes will be labeled:\n"
544 "==============\n"));
545 for (vl=vol_list; vl; vl=vl->next) {
546 if (!vl->VolName || !slot_list[vl->Slot]) {
549 ua->send_msg("%4d %s\n", vl->Slot, vl->VolName);
551 if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
552 (ua->pint32_val == 0)) {
556 memset(&pr, 0, sizeof(pr));
557 if (!select_pool_dbr(ua, &pr)) {
561 /* Fire off the label requests */
562 for (vl=vol_list; vl; vl=vl->next) {
563 if (!vl->VolName || !slot_list[vl->Slot]) {
567 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
568 media_record_exists = false;
569 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
570 if (mr.VolBytes != 0) {
571 ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
572 vl->Slot, mr.VolumeName);
574 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
575 set_storageid_in_mr(store, &mr);
576 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
577 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
581 media_record_exists = true;
583 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
584 set_storageid_in_mr(store, &mr);
586 * Deal with creating cleaning tape here. Normal tapes created in
587 * send_label_request() below
589 if (is_cleaning_tape(ua, &mr, &pr)) {
590 if (media_record_exists) { /* we update it */
591 mr.VolBytes = 1; /* any bytes to indicate it exists */
592 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
594 set_storageid_in_mr(store, &mr);
595 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
596 ua->error_msg("%s", db_strerror(ua->db));
598 } else { /* create the media record */
599 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
600 ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
603 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
604 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
606 set_storageid_in_mr(store, &mr);
607 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
608 ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
610 pr.NumVols++; /* this is a bit suspect */
611 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
612 ua->error_msg("%s", db_strerror(ua->db));
615 ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
618 continue; /* done, go handle next volume */
620 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
623 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
629 free_vol_list(vol_list);
636 * Check if the Volume name has legal characters
637 * If ua is non-NULL send the message
639 bool is_volume_name_legal(UAContext *ua, const char *name)
643 const char *accept = ":.-_";
645 /* Restrict the characters permitted in the Volume name */
646 for (p=name; *p; p++) {
647 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
651 ua->error_msg(_("Illegal character \"%c\" in a volume name.\n"), *p);
656 if (len >= MAX_NAME_LENGTH) {
658 ua->error_msg(_("Volume name too long.\n"));
664 ua->error_msg(_("Volume name must be at least one character long.\n"));
672 * NOTE! This routine opens the SD socket but leaves it open
674 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
675 POOL_DBR *pr, int relabel, bool media_record_exists,
679 char dev_name[MAX_NAME_LENGTH];
682 uint64_t VolBytes = 0;
684 if (!(sd=open_sd_bsock(ua))) {
687 bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
688 bash_spaces(dev_name);
689 bash_spaces(mr->VolumeName);
690 bash_spaces(mr->MediaType);
691 bash_spaces(pr->Name);
693 bash_spaces(omr->VolumeName);
694 sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
695 "MediaType=%s Slot=%d drive=%d",
696 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
697 mr->MediaType, mr->Slot, drive);
698 ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
699 omr->VolumeName, mr->VolumeName);
701 sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
703 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
705 ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"),
706 mr->VolumeName, mr->Slot);
707 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
708 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
711 while (sd->recv() >= 0) {
713 ua->send_msg("%s", sd->msg);
714 if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes,
720 unbash_spaces(mr->VolumeName);
721 unbash_spaces(mr->MediaType);
722 unbash_spaces(pr->Name);
723 mr->LabelDate = time(NULL);
724 mr->set_label_date = true;
726 /* We know that a freshly labelled DVD has 1 VolParts */
727 /* This does not apply to auto-labelled DVDs. */
731 if (media_record_exists) { /* we update it */
732 mr->VolBytes = VolBytes;
733 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
734 set_storageid_in_mr(ua->jcr->wstore, mr);
735 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
736 ua->error_msg("%s", db_strerror(ua->db));
739 } else { /* create the media record */
740 set_pool_dbr_defaults_in_media_dbr(mr, pr);
741 mr->VolBytes = VolBytes;
742 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
744 set_storageid_in_mr(ua->jcr->wstore, mr);
745 if (db_create_media_record(ua->jcr, ua->db, mr)) {
746 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
747 mr->VolumeName, mr->Slot);
748 /* Update number of volumes in pool */
750 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
751 ua->error_msg("%s", db_strerror(ua->db));
754 ua->error_msg("%s", db_strerror(ua->db));
759 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
764 static BSOCK *open_sd_bsock(UAContext *ua)
766 STORE *store = ua->jcr->wstore;
768 if (!ua->jcr->store_bsock) {
769 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d ...\n"),
770 store->name(), store->address, store->SDport);
771 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
772 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
776 return ua->jcr->store_bsock;
779 static void close_sd_bsock(UAContext *ua)
781 if (ua->jcr->store_bsock) {
782 bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
783 bnet_close(ua->jcr->store_bsock);
784 ua->jcr->store_bsock = NULL;
788 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
790 STORE *store = ua->jcr->wstore;
792 char dev_name[MAX_NAME_LENGTH];
793 char *VolName = NULL;
796 if (!(sd=open_sd_bsock(ua))) {
797 ua->error_msg(_("Could not open SD socket.\n"));
800 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
801 bash_spaces(dev_name);
802 /* Ask for autochanger list of volumes */
803 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
804 Dmsg1(100, "Sent: %s", sd->msg);
806 /* Get Volume name in this Slot */
807 while (sd->recv() >= 0) {
808 ua->send_msg("%s", sd->msg);
809 Dmsg1(100, "Got: %s", sd->msg);
810 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
811 VolName = (char *)malloc(sd->msglen);
812 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
820 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
825 * We get the slot list from the Storage daemon.
826 * If scan is set, we return all slots found,
827 * otherwise, we return only slots with valid barcodes (Volume names)
829 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
831 STORE *store = ua->jcr->wstore;
832 char dev_name[MAX_NAME_LENGTH];
835 vol_list_t *vol_list = NULL;
838 if (!(sd=open_sd_bsock(ua))) {
842 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
843 bash_spaces(dev_name);
844 /* Ask for autochanger list of volumes */
845 bnet_fsend(sd, NT_("autochanger list %s \n"), dev_name);
847 /* Read and organize list of Volumes */
848 while (bnet_recv(sd) >= 0) {
851 strip_trailing_junk(sd->msg);
853 /* Check for returned SD messages */
854 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
855 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
857 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
861 /* Validate Slot: if scanning, otherwise Slot:Barcode */
862 p = strchr(sd->msg, ':');
864 /* Scanning -- require only valid slot */
865 Slot = atoi(sd->msg);
869 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
874 if (p && strlen(p) > 1) {
876 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
879 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
885 if (!is_volume_name_legal(ua, p)) {
888 ua->error_msg(_("Invalid Volume name: %s\n"), sd->msg);
893 /* Add Slot and VolumeName to list */
894 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
898 p++; /* skip separator */
900 vl->VolName = bstrdup(p);
904 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
909 vol_list_t *prev=vol_list;
910 /* Add new entry to the right place in the list */
911 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
912 if (tvl->Slot > vl->Slot) {
913 /* no previous item, update vol_list directly */
914 if (prev == vol_list) {
918 } else { /* replace the previous pointer */
924 /* we are at the end */
938 static void free_vol_list(vol_list_t *vol_list)
943 for (vl=vol_list; vl; ) {
955 * We get the number of slots in the changer from the SD
957 static int get_num_slots_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 slots %s\n"), dev_name);
974 while (sd->recv() >= 0) {
975 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
978 ua->send_msg("%s", sd->msg);
982 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
987 * We get the number of drives in the changer from the SD
989 int get_num_drives_from_SD(UAContext *ua)
991 STORE *store = ua->jcr->wstore;
992 char dev_name[MAX_NAME_LENGTH];
997 if (!(sd=open_sd_bsock(ua))) {
1001 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1002 bash_spaces(dev_name);
1003 /* Ask for autochanger number of slots */
1004 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
1006 while (sd->recv() >= 0) {
1007 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
1010 ua->send_msg("%s", sd->msg);
1014 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
1019 * Check if this is a cleaning tape by comparing the Volume name
1020 * with the Cleaning Prefix. If they match, this is a cleaning
1023 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
1025 /* Find Pool resource */
1026 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
1027 if (!ua->jcr->pool) {
1028 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
1029 pr->Name, mr->VolumeName);
1032 if (ua->jcr->pool->cleaning_prefix == NULL) {
1035 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1036 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1037 strlen(ua->jcr->pool->cleaning_prefix),
1038 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1039 strlen(ua->jcr->pool->cleaning_prefix)));
1040 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1041 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1044 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1046 char ed1[50], ed2[50], ed3[50];
1049 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1050 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1051 const char *slot_api_empty_format="%c|%i||||||||\n";
1053 if (is_volume_name_legal(NULL, vol_name)) {
1054 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1055 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1056 memset(&pr, 0, sizeof(POOL_DBR));
1057 pr.PoolId = mr.PoolId;
1058 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1059 strcpy(pr.Name, "?");
1061 ua->send_msg(slot_api_full_format, type,
1062 Slot, mr.Slot, mr.VolumeName,
1063 edit_uint64(mr.VolBytes, ed1),
1064 mr.VolStatus, mr.MediaType, pr.Name,
1065 edit_uint64(mr.LastWritten, ed2),
1066 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1068 } else { /* Media unknown */
1069 ua->send_msg(slot_api_full_format,
1070 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1075 ua->send_msg(slot_api_empty_format, type, Slot);
1080 * Input (output of mxt-changer listall):
1082 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1083 * D:0:F:2:vol2 or D:Drive num:E
1088 * S:1:F:vol1 S:Slot num:F:Volume Name
1089 * S:2:E or S:Slot num:E
1092 * Import/Export tray slots:
1093 * I:10:F:vol10 I:Slot num:F:Volume Name
1094 * I:11:E or I:Slot num:E
1097 * If a drive is loaded, the slot *should* be empty
1101 * Drive list: D|Drive num|Slot loaded|Volume Name
1106 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1108 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1110 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1112 * TODO: need to merge with status_slots()
1114 void status_content(UAContext *ua, STORE *store)
1118 char dev_name[MAX_NAME_LENGTH];
1119 char vol_name[MAX_NAME_LENGTH];
1121 vol_list_t *vl=NULL, *vol_list = NULL;
1123 if (!(sd=open_sd_bsock(ua))) {
1127 if (!open_client_db(ua)) {
1131 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1132 bash_spaces(dev_name);
1133 /* Ask for autochanger list of volumes */
1134 bnet_fsend(sd, NT_("autochanger listall %s \n"), dev_name);
1136 /* Read and organize list of Drive, Slots and I/O Slots */
1137 while (bnet_recv(sd) >= 0) {
1138 strip_trailing_junk(sd->msg);
1140 /* Check for returned SD messages */
1141 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1142 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1143 sd->msg[4] == ' ') {
1144 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1151 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1152 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1154 /* we print information on the slot if we have a volume name */
1156 /* Add Slot and VolumeName to list */
1157 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1159 vl->VolName = bstrdup(vol_name);
1160 vl->next = vol_list;
1164 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1165 ua->send_msg("D|%d||\n", Drive);
1167 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1168 content_send_info(ua, type, Slot, vol_name);
1170 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1171 /* type can be S (slot) or I (Import/Export slot) */
1172 vol_list_t *prev=NULL;
1173 for (vl = vol_list; vl; vl = vl->next) {
1174 if (vl->Slot == Slot) {
1175 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1177 /* remove the node */
1179 prev->next = vl->next;
1181 vol_list = vl->next;
1189 content_send_info(ua, type, Slot, vol_name);
1192 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1199 * Print slots from AutoChanger
1201 void status_slots(UAContext *ua, STORE *store_r)
1205 vol_list_t *vl, *vol_list = NULL;
1210 /* Slot | Volume | Status | MediaType | Pool */
1211 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1214 status_content(ua, store_r);
1218 if (!open_client_db(ua)) {
1221 store.store = store_r;
1223 pm_strcpy(store.store_source, _("command line"));
1224 set_wstorage(ua->jcr, &store);
1225 get_storage_drive(ua, store.store);
1227 max_slots = get_num_slots_from_SD(ua);
1229 if (max_slots <= 0) {
1230 ua->warning_msg(_("No slots in changer to scan.\n"));
1233 slot_list = (char *)malloc(max_slots+1);
1234 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1239 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1242 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1245 ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
1246 ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1248 /* Walk through the list getting the media records */
1249 for (vl=vol_list; vl; vl=vl->next) {
1250 if (vl->Slot > max_slots) {
1251 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1252 vl->Slot, max_slots);
1255 /* Check if user wants us to look at this slot */
1256 if (!slot_list[vl->Slot]) {
1257 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1261 slot_list[vl->Slot] = 0; /* clear Slot */
1264 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1265 ua->send_msg(slot_hformat,
1267 "?", "?", "?", "?");
1271 /* Hope that slots are ordered */
1272 for (; i < vl->Slot; i++) {
1274 ua->send_msg(slot_hformat,
1275 i, ' ', "", "", "", "");
1280 memset(&mr, 0, sizeof(MEDIA_DBR));
1281 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1283 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1284 memset(&pr, 0, sizeof(POOL_DBR));
1285 pr.PoolId = mr.PoolId;
1286 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1287 strcpy(pr.Name, "?");
1290 /* Print information */
1291 ua->send_msg(slot_hformat,
1292 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1293 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1295 } else { /* TODO: get information from catalog */
1296 ua->send_msg(slot_hformat,
1298 mr.VolumeName, "?", "?", "?");
1302 /* Display the rest of the autochanger
1304 for (; i <= max_slots; i++) {
1306 ua->send_msg(slot_hformat,
1307 i, ' ', "", "", "", "");
1314 free_vol_list(vol_list);