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");
87 if (i == -1) { /* not found */
88 i = find_arg_with_value(ua, "slot");
91 /* scan slot list in ua->argv[i] */
95 strip_trailing_junk(ua->argv[i]);
96 for (p=ua->argv[i]; p && *p; p=e) {
102 /* Check for range */
103 h = strchr(p, '-'); /* range? */
105 msg = _("Negative numbers not permitted\n");
110 if (!is_an_integer(h)) {
111 msg = _("Range end is not integer.\n");
115 if (!is_an_integer(p)) {
116 msg = _("Range start is not an integer.\n");
122 msg = _("Range end not bigger than start.\n");
127 if (!is_an_integer(p)) {
128 msg = _("Input value is not an integer.\n");
133 if (beg <= 0 || end <= 0) {
134 msg = _("Values must be be greater than zero.\n");
137 if (end > num_slots) {
138 msg = _("Slot too large.\n");
141 for (i=beg; i<=end; i++) {
142 slot_list[i] = 1; /* Turn on specified range */
146 /* Turn everything on */
147 for (i=1; i <= num_slots; i++) {
151 Dmsg0(100, "Slots turned on:\n");
152 for (i=1; i <= num_slots; i++) {
154 Dmsg1(100, "%d\n", i);
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 */
246 memset(&mr, 0, sizeof(mr));
249 mr.StorageId = store.store->StorageId;
250 /* Set InChanger to zero for this Slot */
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);
259 memset(&mr, 0, sizeof(mr));
260 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
262 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
263 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
266 mr.StorageId = store.store->StorageId;
268 mr.Enabled = Enabled;
270 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
271 ua->error_msg("%s", db_strerror(ua->db));
274 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
275 mr.VolumeName, mr.Slot);
278 ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
284 ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
285 mr.VolumeName, vl->Slot);
289 memset(&mr, 0, sizeof(mr));
291 mr.StorageId = store.store->StorageId;
293 for (int i=1; i <= max_slots; i++) {
296 /* Set InChanger to zero for this Slot */
297 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
304 free_vol_list(vol_list);
313 * Common routine for both label and relabel
315 static int do_label(UAContext *ua, const char *cmd, int relabel)
319 char dev_name[MAX_NAME_LENGTH];
322 bool print_reminder = true;
323 bool label_barcodes = false;
327 bool media_record_exists = false;
328 static const char *barcode_keyword[] = {
334 memset(&pr, 0, sizeof(pr));
335 if (!open_client_db(ua)) {
339 /* Look for one of the barcode keywords */
340 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
341 /* Now find the keyword in the list */
342 if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
343 *ua->argk[j] = 0; /* zap barcode keyword */
345 label_barcodes = true;
348 store.store = get_storage_resource(ua, true/*use default*/);
352 pm_strcpy(store.store_source, _("command line"));
353 set_wstorage(ua->jcr, &store);
354 drive = get_storage_drive(ua, store.store);
356 if (label_barcodes) {
357 label_from_barcodes(ua, drive);
361 /* If relabel get name of Volume to relabel */
363 /* Check for oldvolume=name */
364 i = find_arg_with_value(ua, "oldvolume");
366 memset(&omr, 0, sizeof(omr));
367 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
368 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
371 ua->error_msg("%s", db_strerror(ua->db));
373 /* No keyword or Vol not found, ask user to select */
374 if (!select_media_dbr(ua, &omr)) {
378 /* Require Volume to be Purged or Recycled */
380 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
381 ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
382 omr.VolumeName, omr.VolStatus);
387 /* Check for volume=NewVolume */
388 i = find_arg_with_value(ua, "volume");
390 pm_strcpy(ua->cmd, ua->argv[i]);
394 /* Get a new Volume name */
396 media_record_exists = false;
397 if (!get_cmd(ua, _("Enter new Volume name: "))) {
401 if (!is_volume_name_legal(ua, ua->cmd)) {
405 memset(&mr, 0, sizeof(mr));
406 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
407 /* If VolBytes are zero the Volume is not labeled */
408 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
409 if (mr.VolBytes != 0) {
410 ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
414 media_record_exists = true;
419 /* If autochanger, request slot */
420 i = find_arg_with_value(ua, "slot");
422 mr.Slot = atoi(ua->argv[i]);
426 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
427 } else if (store.store->autochanger) {
428 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
431 mr.Slot = ua->pint32_val;
435 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
437 mr.StorageId = store.store->StorageId;
439 bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
441 /* Must select Pool if not already done */
442 if (pr.PoolId == 0) {
443 memset(&pr, 0, sizeof(pr));
444 if (!select_pool_dbr(ua, &pr)) {
449 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
452 sd = ua->jcr->store_bsock;
454 /* Delete the old media record */
455 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
456 ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
457 omr.VolumeName, db_strerror(ua->db));
459 ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"),
461 /* Update the number of Volumes in the pool */
463 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
464 ua->error_msg("%s", db_strerror(ua->db));
469 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
470 ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
471 bash_spaces(dev_name);
472 bnet_fsend(sd, "mount %s drive=%d", dev_name, drive);
473 unbash_spaces(dev_name);
474 while (bnet_recv(sd) >= 0) {
475 ua->send_msg("%s", sd->msg);
477 * 3001 OK mount. Device=xxx or
478 * 3001 Mounted Volume vvvv
479 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
480 * 3906 is cannot mount non-tape
481 * So for those, no need to print a reminder
483 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
484 strncmp(sd->msg, "3002 ", 5) == 0 ||
485 strncmp(sd->msg, "3906 ", 5) == 0) {
486 print_reminder = false;
491 if (print_reminder) {
492 ua->info_msg(_("Do not forget to mount the drive!!!\n"));
500 * Request SD to send us the slot:barcodes, then wiffle
501 * through them all labeling them.
503 static void label_from_barcodes(UAContext *ua, int drive)
505 STORE *store = ua->jcr->wstore;
508 vol_list_t *vl, *vol_list = NULL;
509 bool media_record_exists;
514 max_slots = get_num_slots_from_SD(ua);
515 if (max_slots <= 0) {
516 ua->warning_msg(_("No slots in changer to scan.\n"));
519 slot_list = (char *)malloc(max_slots+1);
520 if (!get_user_slot_list(ua, slot_list, max_slots)) {
524 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
527 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
531 /* Display list of Volumes and ask if he really wants to proceed */
532 ua->send_msg(_("The following Volumes will be labeled:\n"
534 "==============\n"));
535 for (vl=vol_list; vl; vl=vl->next) {
536 if (!vl->VolName || !slot_list[vl->Slot]) {
539 ua->send_msg("%4d %s\n", vl->Slot, vl->VolName);
541 if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
542 (ua->pint32_val == 0)) {
546 memset(&pr, 0, sizeof(pr));
547 if (!select_pool_dbr(ua, &pr)) {
550 memset(&omr, 0, sizeof(omr));
552 /* Fire off the label requests */
553 for (vl=vol_list; vl; vl=vl->next) {
554 if (!vl->VolName || !slot_list[vl->Slot]) {
557 memset(&mr, 0, sizeof(mr));
558 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
559 media_record_exists = false;
560 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
561 if (mr.VolBytes != 0) {
562 ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
563 vl->Slot, mr.VolumeName);
565 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
566 mr.StorageId = store->StorageId;
567 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
568 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
572 media_record_exists = true;
574 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
575 mr.StorageId = store->StorageId;
577 * Deal with creating cleaning tape here. Normal tapes created in
578 * send_label_request() below
580 if (is_cleaning_tape(ua, &mr, &pr)) {
581 if (media_record_exists) { /* we update it */
582 mr.VolBytes = 1; /* any bytes to indicate it exists */
583 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
585 mr.StorageId = store->StorageId;
586 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
587 ua->error_msg("%s", db_strerror(ua->db));
589 } else { /* create the media record */
590 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
591 ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
594 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
595 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
597 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
598 ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
600 pr.NumVols++; /* this is a bit suspect */
601 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
602 ua->error_msg("%s", db_strerror(ua->db));
605 ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
608 continue; /* done, go handle next volume */
610 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
613 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
619 free_vol_list(vol_list);
626 * Check if the Volume name has legal characters
627 * If ua is non-NULL send the message
629 bool is_volume_name_legal(UAContext *ua, const char *name)
633 const char *accept = ":.-_";
635 /* Restrict the characters permitted in the Volume name */
636 for (p=name; *p; p++) {
637 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
641 ua->error_msg(_("Illegal character \"%c\" in a volume name.\n"), *p);
646 if (len >= MAX_NAME_LENGTH) {
648 ua->error_msg(_("Volume name too long.\n"));
654 ua->error_msg(_("Volume name must be at least one character long.\n"));
662 * NOTE! This routine opens the SD socket but leaves it open
664 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
665 POOL_DBR *pr, int relabel, bool media_record_exists,
669 char dev_name[MAX_NAME_LENGTH];
672 uint64_t VolBytes = 0;
674 if (!(sd=open_sd_bsock(ua))) {
677 bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
678 bash_spaces(dev_name);
679 bash_spaces(mr->VolumeName);
680 bash_spaces(mr->MediaType);
681 bash_spaces(pr->Name);
683 bash_spaces(omr->VolumeName);
684 sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
685 "MediaType=%s Slot=%d drive=%d",
686 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
687 mr->MediaType, mr->Slot, drive);
688 ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
689 omr->VolumeName, mr->VolumeName);
691 sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
693 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
695 ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"),
696 mr->VolumeName, mr->Slot);
697 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
698 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
701 while (sd->recv() >= 0) {
703 ua->send_msg("%s", sd->msg);
704 if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes,
710 unbash_spaces(mr->VolumeName);
711 unbash_spaces(mr->MediaType);
712 unbash_spaces(pr->Name);
713 mr->LabelDate = time(NULL);
714 mr->set_label_date = true;
716 /* We know that a freshly labelled DVD has 1 VolParts */
717 /* This does not apply to auto-labelled DVDs. */
721 if (media_record_exists) { /* we update it */
722 mr->VolBytes = VolBytes;
723 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
724 mr->StorageId = ua->jcr->wstore->StorageId;
725 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
726 ua->error_msg("%s", db_strerror(ua->db));
729 } else { /* create the media record */
730 set_pool_dbr_defaults_in_media_dbr(mr, pr);
731 mr->VolBytes = VolBytes;
732 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
733 mr->StorageId = ua->jcr->wstore->StorageId;
735 if (db_create_media_record(ua->jcr, ua->db, mr)) {
736 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
737 mr->VolumeName, mr->Slot);
738 /* Update number of volumes in pool */
740 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
741 ua->error_msg("%s", db_strerror(ua->db));
744 ua->error_msg("%s", db_strerror(ua->db));
749 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
754 static BSOCK *open_sd_bsock(UAContext *ua)
756 STORE *store = ua->jcr->wstore;
758 if (!ua->jcr->store_bsock) {
759 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d ...\n"),
760 store->name(), store->address, store->SDport);
761 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
762 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
766 return ua->jcr->store_bsock;
769 static void close_sd_bsock(UAContext *ua)
771 if (ua->jcr->store_bsock) {
772 bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
773 bnet_close(ua->jcr->store_bsock);
774 ua->jcr->store_bsock = NULL;
778 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
780 STORE *store = ua->jcr->wstore;
782 char dev_name[MAX_NAME_LENGTH];
783 char *VolName = NULL;
786 if (!(sd=open_sd_bsock(ua))) {
787 ua->error_msg(_("Could not open SD socket.\n"));
790 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
791 bash_spaces(dev_name);
792 /* Ask for autochanger list of volumes */
793 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
794 Dmsg1(100, "Sent: %s", sd->msg);
796 /* Get Volume name in this Slot */
797 while (sd->recv() >= 0) {
798 ua->send_msg("%s", sd->msg);
799 Dmsg1(100, "Got: %s", sd->msg);
800 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
801 VolName = (char *)malloc(sd->msglen);
802 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
810 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
815 * We get the slot list from the Storage daemon.
816 * If scan is set, we return all slots found,
817 * otherwise, we return only slots with valid barcodes (Volume names)
819 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
821 STORE *store = ua->jcr->wstore;
822 char dev_name[MAX_NAME_LENGTH];
825 vol_list_t *vol_list = NULL;
828 if (!(sd=open_sd_bsock(ua))) {
832 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
833 bash_spaces(dev_name);
834 /* Ask for autochanger list of volumes */
835 bnet_fsend(sd, NT_("autochanger list %s \n"), dev_name);
837 /* Read and organize list of Volumes */
838 while (bnet_recv(sd) >= 0) {
841 strip_trailing_junk(sd->msg);
843 /* Check for returned SD messages */
844 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
845 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
847 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
851 /* Validate Slot: if scanning, otherwise Slot:Barcode */
852 p = strchr(sd->msg, ':');
854 /* Scanning -- require only valid slot */
855 Slot = atoi(sd->msg);
859 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
864 if (p && strlen(p) > 1) {
866 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
869 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
875 if (!is_volume_name_legal(ua, p)) {
878 ua->error_msg(_("Invalid Volume name: %s\n"), sd->msg);
883 /* Add Slot and VolumeName to list */
884 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
888 p++; /* skip separator */
890 vl->VolName = bstrdup(p);
894 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
899 vol_list_t *prev=vol_list;
900 /* Add new entry to the right place in the list */
901 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
902 if (tvl->Slot > vl->Slot) {
903 /* no previous item, update vol_list directly */
904 if (prev == vol_list) {
908 } else { /* replace the previous pointer */
914 /* we are at the end */
928 static void free_vol_list(vol_list_t *vol_list)
933 for (vl=vol_list; vl; ) {
945 * We get the number of slots in the changer from the SD
947 static int get_num_slots_from_SD(UAContext *ua)
949 STORE *store = ua->jcr->wstore;
950 char dev_name[MAX_NAME_LENGTH];
955 if (!(sd=open_sd_bsock(ua))) {
959 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
960 bash_spaces(dev_name);
961 /* Ask for autochanger number of slots */
962 sd->fsend(NT_("autochanger slots %s\n"), dev_name);
964 while (sd->recv() >= 0) {
965 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
968 ua->send_msg("%s", sd->msg);
972 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
977 * We get the number of drives in the changer from the SD
979 int get_num_drives_from_SD(UAContext *ua)
981 STORE *store = ua->jcr->wstore;
982 char dev_name[MAX_NAME_LENGTH];
987 if (!(sd=open_sd_bsock(ua))) {
991 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
992 bash_spaces(dev_name);
993 /* Ask for autochanger number of slots */
994 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
996 while (sd->recv() >= 0) {
997 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
1000 ua->send_msg("%s", sd->msg);
1004 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
1009 * Check if this is a cleaning tape by comparing the Volume name
1010 * with the Cleaning Prefix. If they match, this is a cleaning
1013 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
1015 /* Find Pool resource */
1016 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
1017 if (!ua->jcr->pool) {
1018 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
1019 pr->Name, mr->VolumeName);
1022 if (ua->jcr->pool->cleaning_prefix == NULL) {
1025 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1026 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1027 strlen(ua->jcr->pool->cleaning_prefix),
1028 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1029 strlen(ua->jcr->pool->cleaning_prefix)));
1030 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1031 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1034 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1036 char ed1[50], ed2[50], ed3[50];
1039 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1040 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1041 const char *slot_api_empty_format="%c|%i||||||||\n";
1043 if (is_volume_name_legal(NULL, vol_name)) {
1044 memset(&mr, 0, sizeof(mr));
1045 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1046 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1047 memset(&pr, 0, sizeof(POOL_DBR));
1048 pr.PoolId = mr.PoolId;
1049 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1050 strcpy(pr.Name, "?");
1052 ua->send_msg(slot_api_full_format, type,
1053 Slot, mr.Slot, mr.VolumeName,
1054 edit_uint64(mr.VolBytes, ed1),
1055 mr.VolStatus, mr.MediaType, pr.Name,
1056 edit_uint64(mr.LastWritten, ed2),
1057 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1059 } else { /* Media unknown */
1060 ua->send_msg(slot_api_full_format,
1061 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1066 ua->send_msg(slot_api_empty_format, type, Slot);
1071 * Input (output of mxt-changer listall):
1073 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1074 * D:0:F:2:vol2 or D:Drive num:E
1079 * S:1:F:vol1 S:Slot num:F:Volume Name
1080 * S:2:E or S:Slot num:E
1083 * Import/Export tray slots:
1084 * I:10:F:vol10 I:Slot num:F:Volume Name
1085 * I:11:E or I:Slot num:E
1088 * If a drive is loaded, the slot *should* be empty
1092 * Drive list: D|Drive num|Slot loaded|Volume Name
1097 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1099 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1101 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1103 * TODO: need to merge with status_slots()
1105 void status_content(UAContext *ua, STORE *store)
1109 char dev_name[MAX_NAME_LENGTH];
1110 char vol_name[MAX_NAME_LENGTH];
1112 vol_list_t *vl=NULL, *vol_list = NULL;
1114 if (!(sd=open_sd_bsock(ua))) {
1118 if (!open_client_db(ua)) {
1122 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1123 bash_spaces(dev_name);
1124 /* Ask for autochanger list of volumes */
1125 bnet_fsend(sd, NT_("autochanger listall %s \n"), dev_name);
1127 /* Read and organize list of Drive, Slots and I/O Slots */
1128 while (bnet_recv(sd) >= 0) {
1129 strip_trailing_junk(sd->msg);
1131 /* Check for returned SD messages */
1132 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1133 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1134 sd->msg[4] == ' ') {
1135 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1142 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1143 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1145 /* we print information on the slot if we have a volume name */
1147 /* Add Slot and VolumeName to list */
1148 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1150 vl->VolName = bstrdup(vol_name);
1151 vl->next = vol_list;
1155 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1156 ua->send_msg("D|%d||\n", Drive);
1158 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1159 content_send_info(ua, type, Slot, vol_name);
1161 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1162 /* type can be S (slot) or I (Import/Export slot) */
1163 vol_list_t *prev=NULL;
1164 for (vl = vol_list; vl; vl = vl->next) {
1165 if (vl->Slot == Slot) {
1166 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1168 /* remove the node */
1170 prev->next = vl->next;
1172 vol_list = vl->next;
1180 content_send_info(ua, type, Slot, vol_name);
1183 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1190 * Print slots from AutoChanger
1192 void status_slots(UAContext *ua, STORE *store_r)
1196 vol_list_t *vl, *vol_list = NULL;
1202 /* Slot | Volume | Status | MediaType | Pool */
1203 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1206 status_content(ua, store_r);
1210 if (!open_client_db(ua)) {
1213 store.store = store_r;
1215 pm_strcpy(store.store_source, _("command line"));
1216 set_wstorage(ua->jcr, &store);
1217 drive = get_storage_drive(ua, store.store);
1219 max_slots = get_num_slots_from_SD(ua);
1221 if (max_slots <= 0) {
1222 ua->warning_msg(_("No slots in changer to scan.\n"));
1225 slot_list = (char *)malloc(max_slots+1);
1226 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1231 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1234 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1237 ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
1238 ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1240 /* Walk through the list getting the media records */
1241 for (vl=vol_list; vl; vl=vl->next) {
1242 if (vl->Slot > max_slots) {
1243 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1244 vl->Slot, max_slots);
1247 /* Check if user wants us to look at this slot */
1248 if (!slot_list[vl->Slot]) {
1249 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1253 slot_list[vl->Slot] = 0; /* clear Slot */
1256 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1257 ua->send_msg(slot_hformat,
1259 "?", "?", "?", "?");
1263 /* Hope that slots are ordered */
1264 for (; i < vl->Slot; i++) {
1266 ua->send_msg(slot_hformat,
1267 i, ' ', "", "", "", "");
1272 memset(&mr, 0, sizeof(mr));
1273 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1275 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1276 memset(&pr, 0, sizeof(POOL_DBR));
1277 pr.PoolId = mr.PoolId;
1278 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1279 strcpy(pr.Name, "?");
1283 /* Print information */
1284 ua->send_msg(slot_hformat,
1285 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1286 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1288 } else { /* TODO: get information from catalog */
1289 ua->send_msg(slot_hformat,
1291 mr.VolumeName, "?", "?", "?");
1296 /* Display the rest of the autochanger
1298 for (; i <= max_slots; i++) {
1300 ua->send_msg(slot_hformat,
1301 i, ' ', "", "", "", "");
1308 free_vol_list(vol_list);