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 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
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);
160 Dmsg1(100, "Problem with user selection ERR=%s\n", msg);
165 * Update Slots corresponding to Volumes in autochanger
167 void update_slots(UAContext *ua)
170 vol_list_t *vl, *vol_list = NULL;
181 if (!open_client_db(ua)) {
184 store.store = get_storage_resource(ua, true/*arg is storage*/);
188 pm_strcpy(store.store_source, _("command line"));
189 set_wstorage(ua->jcr, &store);
190 drive = get_storage_drive(ua, store.store);
192 scan = find_arg(ua, NT_("scan")) >= 0;
193 if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
194 Enabled = get_enabled(ua, ua->argv[i]);
200 have_enabled = false;
203 max_slots = get_num_slots_from_SD(ua);
204 Dmsg1(100, "max_slots=%d\n", max_slots);
205 if (max_slots <= 0) {
206 ua->warning_msg(_("No slots in changer to scan.\n"));
209 slot_list = (char *)malloc(max_slots+1);
210 if (!get_user_slot_list(ua, slot_list, max_slots)) {
215 vol_list = get_vol_list_from_SD(ua, scan);
218 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
222 /* First zap out any InChanger with StorageId=0 */
223 db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);
225 /* Walk through the list updating the media records */
226 for (vl=vol_list; vl; vl=vl->next) {
227 if (vl->Slot > max_slots) {
228 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
229 vl->Slot, max_slots);
232 /* Check if user wants us to look at this slot */
233 if (!slot_list[vl->Slot]) {
234 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
237 /* If scanning, we read the label rather than the barcode */
243 vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
244 Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
246 slot_list[vl->Slot] = 0; /* clear Slot */
247 memset(&mr, 0, sizeof(mr));
250 mr.StorageId = store.store->StorageId;
251 /* Set InChanger to zero for this Slot */
253 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
256 Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
257 ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
260 memset(&mr, 0, sizeof(mr));
261 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
263 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
264 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
267 mr.StorageId = store.store->StorageId;
269 mr.Enabled = Enabled;
271 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
272 ua->error_msg("%s", db_strerror(ua->db));
275 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
276 mr.VolumeName, mr.Slot);
279 ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
285 ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
286 mr.VolumeName, vl->Slot);
290 memset(&mr, 0, sizeof(mr));
292 mr.StorageId = store.store->StorageId;
294 for (int i=1; i <= max_slots; i++) {
297 /* Set InChanger to zero for this Slot */
298 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
305 free_vol_list(vol_list);
314 * Common routine for both label and relabel
316 static int do_label(UAContext *ua, const char *cmd, int relabel)
320 char dev_name[MAX_NAME_LENGTH];
323 bool print_reminder = true;
324 bool label_barcodes = false;
328 bool media_record_exists = false;
329 static const char *barcode_keyword[] = {
335 memset(&pr, 0, sizeof(pr));
336 if (!open_client_db(ua)) {
340 /* Look for one of the barcode keywords */
341 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
342 /* Now find the keyword in the list */
343 if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
344 *ua->argk[j] = 0; /* zap barcode keyword */
346 label_barcodes = true;
349 store.store = get_storage_resource(ua, true/*use default*/);
353 pm_strcpy(store.store_source, _("command line"));
354 set_wstorage(ua->jcr, &store);
355 drive = get_storage_drive(ua, store.store);
357 if (label_barcodes) {
358 label_from_barcodes(ua, drive);
362 /* If relabel get name of Volume to relabel */
364 /* Check for oldvolume=name */
365 i = find_arg_with_value(ua, "oldvolume");
367 memset(&omr, 0, sizeof(omr));
368 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
369 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
372 ua->error_msg("%s", db_strerror(ua->db));
374 /* No keyword or Vol not found, ask user to select */
375 if (!select_media_dbr(ua, &omr)) {
379 /* Require Volume to be Purged or Recycled */
381 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
382 ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
383 omr.VolumeName, omr.VolStatus);
388 /* Check for volume=NewVolume */
389 i = find_arg_with_value(ua, "volume");
391 pm_strcpy(ua->cmd, ua->argv[i]);
395 /* Get a new Volume name */
397 media_record_exists = false;
398 if (!get_cmd(ua, _("Enter new Volume name: "))) {
402 if (!is_volume_name_legal(ua, ua->cmd)) {
406 memset(&mr, 0, sizeof(mr));
407 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
408 /* If VolBytes are zero the Volume is not labeled */
409 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
410 if (mr.VolBytes != 0) {
411 ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
415 media_record_exists = true;
420 /* If autochanger, request slot */
421 i = find_arg_with_value(ua, "slot");
423 mr.Slot = atoi(ua->argv[i]);
427 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
428 } else if (store.store->autochanger) {
429 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
432 mr.Slot = ua->pint32_val;
436 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
438 mr.StorageId = store.store->StorageId;
440 bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
442 /* Must select Pool if not already done */
443 if (pr.PoolId == 0) {
444 memset(&pr, 0, sizeof(pr));
445 if (!select_pool_dbr(ua, &pr)) {
450 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
453 sd = ua->jcr->store_bsock;
455 /* Delete the old media record */
456 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
457 ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
458 omr.VolumeName, db_strerror(ua->db));
460 ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"),
462 /* Update the number of Volumes in the pool */
464 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
465 ua->error_msg("%s", db_strerror(ua->db));
470 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
471 ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
472 bash_spaces(dev_name);
473 bnet_fsend(sd, "mount %s drive=%d", dev_name, drive);
474 unbash_spaces(dev_name);
475 while (bnet_recv(sd) >= 0) {
476 ua->send_msg("%s", sd->msg);
478 * 3001 OK mount. Device=xxx or
479 * 3001 Mounted Volume vvvv
480 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
481 * 3906 is cannot mount non-tape
482 * So for those, no need to print a reminder
484 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
485 strncmp(sd->msg, "3002 ", 5) == 0 ||
486 strncmp(sd->msg, "3906 ", 5) == 0) {
487 print_reminder = false;
492 if (print_reminder) {
493 ua->info_msg(_("Do not forget to mount the drive!!!\n"));
501 * Request SD to send us the slot:barcodes, then wiffle
502 * through them all labeling them.
504 static void label_from_barcodes(UAContext *ua, int drive)
506 STORE *store = ua->jcr->wstore;
509 vol_list_t *vl, *vol_list = NULL;
510 bool media_record_exists;
515 max_slots = get_num_slots_from_SD(ua);
516 if (max_slots <= 0) {
517 ua->warning_msg(_("No slots in changer to scan.\n"));
520 slot_list = (char *)malloc(max_slots+1);
521 if (!get_user_slot_list(ua, slot_list, max_slots)) {
525 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
528 ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
532 /* Display list of Volumes and ask if he really wants to proceed */
533 ua->send_msg(_("The following Volumes will be labeled:\n"
535 "==============\n"));
536 for (vl=vol_list; vl; vl=vl->next) {
537 if (!vl->VolName || !slot_list[vl->Slot]) {
540 ua->send_msg("%4d %s\n", vl->Slot, vl->VolName);
542 if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
543 (ua->pint32_val == 0)) {
547 memset(&pr, 0, sizeof(pr));
548 if (!select_pool_dbr(ua, &pr)) {
551 memset(&omr, 0, sizeof(omr));
553 /* Fire off the label requests */
554 for (vl=vol_list; vl; vl=vl->next) {
555 if (!vl->VolName || !slot_list[vl->Slot]) {
558 memset(&mr, 0, sizeof(mr));
559 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
560 media_record_exists = false;
561 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
562 if (mr.VolBytes != 0) {
563 ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
564 vl->Slot, mr.VolumeName);
566 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
567 mr.StorageId = store->StorageId;
568 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
569 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
573 media_record_exists = true;
575 mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */
576 mr.StorageId = store->StorageId;
578 * Deal with creating cleaning tape here. Normal tapes created in
579 * send_label_request() below
581 if (is_cleaning_tape(ua, &mr, &pr)) {
582 if (media_record_exists) { /* we update it */
583 mr.VolBytes = 1; /* any bytes to indicate it exists */
584 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
586 mr.StorageId = store->StorageId;
587 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
588 ua->error_msg("%s", db_strerror(ua->db));
590 } else { /* create the media record */
591 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
592 ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
595 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
596 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
598 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
599 ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
601 pr.NumVols++; /* this is a bit suspect */
602 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
603 ua->error_msg("%s", db_strerror(ua->db));
606 ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
609 continue; /* done, go handle next volume */
611 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
614 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
620 free_vol_list(vol_list);
627 * Check if the Volume name has legal characters
628 * If ua is non-NULL send the message
630 bool is_volume_name_legal(UAContext *ua, const char *name)
634 const char *accept = ":.-_";
636 /* Restrict the characters permitted in the Volume name */
637 for (p=name; *p; p++) {
638 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
642 ua->error_msg(_("Illegal character \"%c\" in a volume name.\n"), *p);
647 if (len >= MAX_NAME_LENGTH) {
649 ua->error_msg(_("Volume name too long.\n"));
655 ua->error_msg(_("Volume name must be at least one character long.\n"));
663 * NOTE! This routine opens the SD socket but leaves it open
665 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
666 POOL_DBR *pr, int relabel, bool media_record_exists,
670 char dev_name[MAX_NAME_LENGTH];
673 uint64_t VolBytes = 0;
675 if (!(sd=open_sd_bsock(ua))) {
678 bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
679 bash_spaces(dev_name);
680 bash_spaces(mr->VolumeName);
681 bash_spaces(mr->MediaType);
682 bash_spaces(pr->Name);
684 bash_spaces(omr->VolumeName);
685 sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
686 "MediaType=%s Slot=%d drive=%d",
687 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
688 mr->MediaType, mr->Slot, drive);
689 ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
690 omr->VolumeName, mr->VolumeName);
692 sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
694 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
696 ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"),
697 mr->VolumeName, mr->Slot);
698 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
699 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
702 while (sd->recv() >= 0) {
704 ua->send_msg("%s", sd->msg);
705 if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes,
711 unbash_spaces(mr->VolumeName);
712 unbash_spaces(mr->MediaType);
713 unbash_spaces(pr->Name);
714 mr->LabelDate = time(NULL);
715 mr->set_label_date = true;
717 /* We know that a freshly labelled DVD has 1 VolParts */
718 /* This does not apply to auto-labelled DVDs. */
722 if (media_record_exists) { /* we update it */
723 mr->VolBytes = VolBytes;
724 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
725 mr->StorageId = ua->jcr->wstore->StorageId;
726 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
727 ua->error_msg("%s", db_strerror(ua->db));
730 } else { /* create the media record */
731 set_pool_dbr_defaults_in_media_dbr(mr, pr);
732 mr->VolBytes = VolBytes;
733 mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
734 mr->StorageId = ua->jcr->wstore->StorageId;
736 if (db_create_media_record(ua->jcr, ua->db, mr)) {
737 ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
738 mr->VolumeName, mr->Slot);
739 /* Update number of volumes in pool */
741 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
742 ua->error_msg("%s", db_strerror(ua->db));
745 ua->error_msg("%s", db_strerror(ua->db));
750 ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
755 static BSOCK *open_sd_bsock(UAContext *ua)
757 STORE *store = ua->jcr->wstore;
759 if (!ua->jcr->store_bsock) {
760 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d ...\n"),
761 store->name(), store->address, store->SDport);
762 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
763 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
767 return ua->jcr->store_bsock;
770 static void close_sd_bsock(UAContext *ua)
772 if (ua->jcr->store_bsock) {
773 bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
774 bnet_close(ua->jcr->store_bsock);
775 ua->jcr->store_bsock = NULL;
779 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
781 STORE *store = ua->jcr->wstore;
783 char dev_name[MAX_NAME_LENGTH];
784 char *VolName = NULL;
787 if (!(sd=open_sd_bsock(ua))) {
788 ua->error_msg(_("Could not open SD socket.\n"));
791 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
792 bash_spaces(dev_name);
793 /* Ask for autochanger list of volumes */
794 sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
795 Dmsg1(100, "Sent: %s", sd->msg);
797 /* Get Volume name in this Slot */
798 while (sd->recv() >= 0) {
799 ua->send_msg("%s", sd->msg);
800 Dmsg1(100, "Got: %s", sd->msg);
801 if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
802 VolName = (char *)malloc(sd->msglen);
803 if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
811 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
816 * We get the slot list from the Storage daemon.
817 * If scan is set, we return all slots found,
818 * otherwise, we return only slots with valid barcodes (Volume names)
820 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
822 STORE *store = ua->jcr->wstore;
823 char dev_name[MAX_NAME_LENGTH];
826 vol_list_t *vol_list = NULL;
829 if (!(sd=open_sd_bsock(ua))) {
833 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
834 bash_spaces(dev_name);
835 /* Ask for autochanger list of volumes */
836 bnet_fsend(sd, NT_("autochanger list %s \n"), dev_name);
838 /* Read and organize list of Volumes */
839 while (bnet_recv(sd) >= 0) {
842 strip_trailing_junk(sd->msg);
844 /* Check for returned SD messages */
845 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
846 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
848 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
852 /* Validate Slot: if scanning, otherwise Slot:Barcode */
853 p = strchr(sd->msg, ':');
855 /* Scanning -- require only valid slot */
856 Slot = atoi(sd->msg);
860 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
865 if (p && strlen(p) > 1) {
867 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
870 ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
876 if (!is_volume_name_legal(ua, p)) {
879 ua->error_msg(_("Invalid Volume name: %s\n"), sd->msg);
884 /* Add Slot and VolumeName to list */
885 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
889 p++; /* skip separator */
891 vl->VolName = bstrdup(p);
895 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
900 vol_list_t *prev=vol_list;
901 /* Add new entry to the right place in the list */
902 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
903 if (tvl->Slot > vl->Slot) {
904 /* no previous item, update vol_list directly */
905 if (prev == vol_list) {
909 } else { /* replace the previous pointer */
915 /* we are at the end */
929 static void free_vol_list(vol_list_t *vol_list)
934 for (vl=vol_list; vl; ) {
946 * We get the number of slots in the changer from the SD
948 static int get_num_slots_from_SD(UAContext *ua)
950 STORE *store = ua->jcr->wstore;
951 char dev_name[MAX_NAME_LENGTH];
956 if (!(sd=open_sd_bsock(ua))) {
960 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
961 bash_spaces(dev_name);
962 /* Ask for autochanger number of slots */
963 sd->fsend(NT_("autochanger slots %s\n"), dev_name);
965 while (sd->recv() >= 0) {
966 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
969 ua->send_msg("%s", sd->msg);
973 ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
978 * We get the number of drives in the changer from the SD
980 int get_num_drives_from_SD(UAContext *ua)
982 STORE *store = ua->jcr->wstore;
983 char dev_name[MAX_NAME_LENGTH];
988 if (!(sd=open_sd_bsock(ua))) {
992 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
993 bash_spaces(dev_name);
994 /* Ask for autochanger number of slots */
995 sd->fsend(NT_("autochanger drives %s\n"), dev_name);
997 while (sd->recv() >= 0) {
998 if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
1001 ua->send_msg("%s", sd->msg);
1005 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
1010 * Check if this is a cleaning tape by comparing the Volume name
1011 * with the Cleaning Prefix. If they match, this is a cleaning
1014 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
1016 /* Find Pool resource */
1017 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
1018 if (!ua->jcr->pool) {
1019 ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
1020 pr->Name, mr->VolumeName);
1023 if (ua->jcr->pool->cleaning_prefix == NULL) {
1026 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1027 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1028 strlen(ua->jcr->pool->cleaning_prefix),
1029 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1030 strlen(ua->jcr->pool->cleaning_prefix)));
1031 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1032 strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1035 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1037 char ed1[50], ed2[50], ed3[50];
1040 /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1041 const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1042 const char *slot_api_empty_format="%c|%i||||||||\n";
1044 if (is_volume_name_legal(NULL, vol_name)) {
1045 memset(&mr, 0, sizeof(mr));
1046 bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1047 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1048 memset(&pr, 0, sizeof(POOL_DBR));
1049 pr.PoolId = mr.PoolId;
1050 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1051 strcpy(pr.Name, "?");
1053 ua->send_msg(slot_api_full_format, type,
1054 Slot, mr.Slot, mr.VolumeName,
1055 edit_uint64(mr.VolBytes, ed1),
1056 mr.VolStatus, mr.MediaType, pr.Name,
1057 edit_uint64(mr.LastWritten, ed2),
1058 edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1060 } else { /* Media unknown */
1061 ua->send_msg(slot_api_full_format,
1062 type, Slot, 0, mr.VolumeName, "?", "?", "?", "?",
1067 ua->send_msg(slot_api_empty_format, type, Slot);
1072 * Input (output of mxt-changer listall):
1074 * Drive content: D:Drive num:F:Slot loaded:Volume Name
1075 * D:0:F:2:vol2 or D:Drive num:E
1080 * S:1:F:vol1 S:Slot num:F:Volume Name
1081 * S:2:E or S:Slot num:E
1084 * Import/Export tray slots:
1085 * I:10:F:vol10 I:Slot num:F:Volume Name
1086 * I:11:E or I:Slot num:E
1089 * If a drive is loaded, the slot *should* be empty
1093 * Drive list: D|Drive num|Slot loaded|Volume Name
1098 * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1100 * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1102 * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1104 * TODO: need to merge with status_slots()
1106 void status_content(UAContext *ua, STORE *store)
1110 char dev_name[MAX_NAME_LENGTH];
1111 char vol_name[MAX_NAME_LENGTH];
1113 vol_list_t *vl=NULL, *vol_list = NULL;
1115 if (!(sd=open_sd_bsock(ua))) {
1119 if (!open_client_db(ua)) {
1123 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1124 bash_spaces(dev_name);
1125 /* Ask for autochanger list of volumes */
1126 bnet_fsend(sd, NT_("autochanger listall %s \n"), dev_name);
1128 /* Read and organize list of Drive, Slots and I/O Slots */
1129 while (bnet_recv(sd) >= 0) {
1130 strip_trailing_junk(sd->msg);
1132 /* Check for returned SD messages */
1133 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
1134 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1135 sd->msg[4] == ' ') {
1136 ua->send_msg("%s\n", sd->msg); /* pass them on to user */
1143 if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1144 ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1146 /* we print information on the slot if we have a volume name */
1148 /* Add Slot and VolumeName to list */
1149 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1151 vl->VolName = bstrdup(vol_name);
1152 vl->next = vol_list;
1156 } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1157 ua->send_msg("D|%d||\n", Drive);
1159 } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1160 content_send_info(ua, type, Slot, vol_name);
1162 } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1163 /* type can be S (slot) or I (Import/Export slot) */
1164 vol_list_t *prev=NULL;
1165 for (vl = vol_list; vl; vl = vl->next) {
1166 if (vl->Slot == Slot) {
1167 bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1169 /* remove the node */
1171 prev->next = vl->next;
1173 vol_list = vl->next;
1181 content_send_info(ua, type, Slot, vol_name);
1184 Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1191 * Print slots from AutoChanger
1193 void status_slots(UAContext *ua, STORE *store_r)
1197 vol_list_t *vl, *vol_list = NULL;
1203 /* Slot | Volume | Status | MediaType | Pool */
1204 const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1207 status_content(ua, store_r);
1211 if (!open_client_db(ua)) {
1214 store.store = store_r;
1216 pm_strcpy(store.store_source, _("command line"));
1217 set_wstorage(ua->jcr, &store);
1218 drive = get_storage_drive(ua, store.store);
1220 max_slots = get_num_slots_from_SD(ua);
1222 if (max_slots <= 0) {
1223 ua->warning_msg(_("No slots in changer to scan.\n"));
1226 slot_list = (char *)malloc(max_slots+1);
1227 if (!get_user_slot_list(ua, slot_list, max_slots)) {
1232 vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1235 ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1238 ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n"));
1239 ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1241 /* Walk through the list getting the media records */
1242 for (vl=vol_list; vl; vl=vl->next) {
1243 if (vl->Slot > max_slots) {
1244 ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1245 vl->Slot, max_slots);
1248 /* Check if user wants us to look at this slot */
1249 if (!slot_list[vl->Slot]) {
1250 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1254 slot_list[vl->Slot] = 0; /* clear Slot */
1257 Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1258 ua->send_msg(slot_hformat,
1260 "?", "?", "?", "?");
1264 /* Hope that slots are ordered */
1265 for (; i < vl->Slot; i++) {
1267 ua->send_msg(slot_hformat,
1268 i, ' ', "", "", "", "");
1273 memset(&mr, 0, sizeof(mr));
1274 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1276 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1277 memset(&pr, 0, sizeof(POOL_DBR));
1278 pr.PoolId = mr.PoolId;
1279 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1280 strcpy(pr.Name, "?");
1284 /* Print information */
1285 ua->send_msg(slot_hformat,
1286 vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1287 mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1289 } else { /* TODO: get information from catalog */
1290 ua->send_msg(slot_hformat,
1292 mr.VolumeName, "?", "?", "?");
1297 /* Display the rest of the autochanger
1299 for (; i <= max_slots; i++) {
1301 ua->send_msg(slot_hformat,
1302 i, ' ', "", "", "", "");
1309 free_vol_list(vol_list);