3 * Bacula Director -- Tape labeling commands
5 * Kern Sibbald, April MMIII
10 Copyright (C) 2003-2005 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
27 /* Slot list definition */
28 typedef struct s_vol_list {
29 struct s_vol_list *next;
35 /* Forward referenced functions */
36 static int do_label(UAContext *ua, const char *cmd, int relabel);
37 static void label_from_barcodes(UAContext *ua, int drive);
38 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
39 POOL_DBR *pr, int relabel, bool media_record_exits, int drive);
40 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan);
41 static void free_vol_list(vol_list_t *vol_list);
42 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr);
43 static BSOCK *open_sd_bsock(UAContext *ua);
44 static void close_sd_bsock(UAContext *ua);
45 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive);
46 static int get_num_slots_from_SD(UAContext *ua);
52 * label storage=xxx volume=vvv
54 int label_cmd(UAContext *ua, const char *cmd)
56 return do_label(ua, cmd, 0); /* standard label */
59 int relabel_cmd(UAContext *ua, const char *cmd)
61 return do_label(ua, cmd, 1); /* relabel tape */
64 static bool get_user_slot_list(UAContext *ua, char *slot_list, int num_slots)
69 for (int i=0; i <= num_slots; i++) {
72 i = find_arg_with_value(ua, "slots");
74 /* scan slot list in ua->argv[i] */
78 strip_trailing_junk(ua->argv[i]);
79 for (p=ua->argv[i]; p && *p; p=e) {
86 h = strchr(p, '-'); /* range? */
88 msg = _("Negative numbers not permitted\n");
93 if (!is_an_integer(h)) {
94 msg = _("Range end is not integer.\n");
98 if (!is_an_integer(p)) {
99 msg = _("Range start is not an integer.\n");
105 msg = _("Range end not bigger than start.\n");
110 if (!is_an_integer(p)) {
111 msg = _("Input value is not an integer.\n");
116 if (beg <= 0 || end <= 0) {
117 msg = _("Values must be be greater than zero.\n");
120 if (end >= num_slots) {
121 msg = _("Slot too large.\n");
124 for (i=beg; i<=end; i++) {
125 slot_list[i] = 1; /* Turn on specified range */
129 /* Turn everything on */
130 for (i=0; i <= num_slots; i++) {
135 printf("Slots turned on:\n");
136 for (i=1; i <= num_slots; i++) {
149 * Update Slots corresponding to Volumes in autochanger
151 int update_slots(UAContext *ua)
154 vol_list_t *vl, *vol_list = NULL;
165 store = get_storage_resource(ua, 1);
169 set_storage(ua->jcr, store);
171 scan = find_arg(ua, N_("scan")) >= 0;
173 max_slots = get_num_slots_from_SD(ua);
174 if (max_slots <= 0) {
175 bsendmsg(ua, _("No slots in changer to scan.\n"));
178 slot_list = (char *)malloc(max_slots+1);
179 if (!get_user_slot_list(ua, slot_list, max_slots)) {
184 vol_list = get_vol_list_from_SD(ua, scan);
187 bsendmsg(ua, _("No Volumes found to label, or no barcodes.\n"));
191 /* Walk through the list updating the media records */
192 for (vl=vol_list; vl; vl=vl->next) {
193 if (vl->Slot > max_slots) {
194 bsendmsg(ua, _("Slot %d larger than max %d ignored.\n"),
195 vl->Slot, max_slots);
198 /* Check if user wants us to look at this slot */
199 if (!slot_list[vl->Slot]) {
200 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
203 /* If scanning, we read the label rather than the barcode */
209 vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
210 Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
212 slot_list[vl->Slot] = 0; /* clear Slot */
214 Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
215 memset(&mr, 0, sizeof(mr));
218 mr.StorageId = store->StorageId;
219 /* Set InChanger to zero for this Slot */
221 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
223 bsendmsg(ua, _("No VolName for Slot=%d set InChanger to zero.\n"), vl->Slot);
226 memset(&mr, 0, sizeof(mr));
227 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
229 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
230 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store->StorageId) {
233 mr.StorageId = store->StorageId;
234 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
235 bsendmsg(ua, "%s", db_strerror(ua->db));
238 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
239 mr.VolumeName, mr.Slot);
242 bsendmsg(ua, _("Catalog record for Volume \"%s\" is up to date.\n"),
248 bsendmsg(ua, _("Record for Volume \"%s\" not found in catalog.\n"),
253 memset(&mr, 0, sizeof(mr));
255 mr.StorageId = store->StorageId;
257 for (int i=1; i <= max_slots; i++) {
260 /* Set InChanger to zero for this Slot */
261 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
268 free_vol_list(vol_list);
277 * Common routine for both label and relabel
279 static int do_label(UAContext *ua, const char *cmd, int relabel)
283 char dev_name[MAX_NAME_LENGTH];
286 bool print_reminder = true;
290 bool media_record_exists = false;
291 static const char *barcode_keyword[] = {
297 memset(&pr, 0, sizeof(pr));
301 store = get_storage_resource(ua, true/*use default*/);
305 drive = ua->int32_val;
306 set_storage(ua->jcr, store);
308 if (!relabel && find_arg_keyword(ua, barcode_keyword) >= 0) {
309 label_from_barcodes(ua, drive);
313 /* If relabel get name of Volume to relabel */
315 /* Check for oldvolume=name */
316 i = find_arg_with_value(ua, "oldvolume");
318 memset(&omr, 0, sizeof(omr));
319 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
320 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
323 bsendmsg(ua, "%s", db_strerror(ua->db));
325 /* No keyword or Vol not found, ask user to select */
326 if (!select_media_dbr(ua, &omr)) {
330 /* Require Volume to be Purged or Recycled */
332 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
333 bsendmsg(ua, _("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
334 omr.VolumeName, omr.VolStatus);
339 /* Check for volume=NewVolume */
340 i = find_arg_with_value(ua, "volume");
342 pm_strcpy(ua->cmd, ua->argv[i]);
346 /* Get a new Volume name */
348 media_record_exists = false;
349 if (!get_cmd(ua, _("Enter new Volume name: "))) {
353 if (!is_volume_name_legal(ua, ua->cmd)) {
357 memset(&mr, 0, sizeof(mr));
358 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
359 /* If VolBytes are zero the Volume is not labeled */
360 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
361 if (mr.VolBytes != 0) {
362 bsendmsg(ua, _("Media record for new Volume \"%s\" already exists.\n"),
366 media_record_exists = true;
371 /* If autochanger, request slot */
372 i = find_arg_with_value(ua, "slot");
374 mr.Slot = atoi(ua->argv[i]);
375 mr.InChanger = 1; /* assumed if we are labeling it */
376 } else if (store->autochanger) {
377 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
380 mr.Slot = ua->pint32_val;
381 mr.InChanger = 1; /* assumed if we are labeling it */
383 mr.StorageId = store->StorageId;
385 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
387 /* Must select Pool if not already done */
388 if (pr.PoolId == 0) {
389 memset(&pr, 0, sizeof(pr));
390 if (!select_pool_dbr(ua, &pr)) {
395 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
398 sd = ua->jcr->store_bsock;
400 /* Delete the old media record */
401 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
402 bsendmsg(ua, _("Delete of Volume \"%s\" failed. ERR=%s"),
403 omr.VolumeName, db_strerror(ua->db));
405 bsendmsg(ua, _("Old volume \"%s\" deleted from catalog.\n"),
407 /* Update the number of Volumes in the pool */
409 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
410 bsendmsg(ua, "%s", db_strerror(ua->db));
415 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
416 bsendmsg(ua, _("Requesting to mount %s ...\n"), dev_name);
417 bash_spaces(dev_name);
418 bnet_fsend(sd, "mount %s drive=%d", dev_name, drive);
419 unbash_spaces(dev_name);
420 while (bnet_recv(sd) >= 0) {
421 bsendmsg(ua, "%s", sd->msg);
423 * 3001 OK mount. Device=xxx or
424 * 3001 Mounted Volume vvvv
425 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
426 * 3906 is cannot mount non-tape
427 * So for those, no need to print a reminder
429 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
430 strncmp(sd->msg, "3002 ", 5) == 0 ||
431 strncmp(sd->msg, "3906 ", 5) == 0) {
432 print_reminder = false;
437 if (print_reminder) {
438 bsendmsg(ua, _("Do not forget to mount the drive!!!\n"));
446 * Request SD to send us the slot:barcodes, then wiffle
447 * through them all labeling them.
449 static void label_from_barcodes(UAContext *ua, int drive)
451 STORE *store = ua->jcr->store;
454 vol_list_t *vl, *vol_list = NULL;
455 bool media_record_exists;
460 max_slots = get_num_slots_from_SD(ua);
461 if (max_slots <= 0) {
462 bsendmsg(ua, _("No slots in changer to scan.\n"));
465 slot_list = (char *)malloc(max_slots+1);
466 if (!get_user_slot_list(ua, slot_list, max_slots)) {
470 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
473 bsendmsg(ua, _("No Volumes found to label, or no barcodes.\n"));
477 /* Display list of Volumes and ask if he really wants to proceed */
478 bsendmsg(ua, _("The following Volumes will be labeled:\n"
480 "==============\n"));
481 for (vl=vol_list; vl; vl=vl->next) {
482 if (!vl->VolName || !slot_list[vl->Slot]) {
485 bsendmsg(ua, "%4d %s\n", vl->Slot, vl->VolName);
487 if (!get_cmd(ua, _("Do you want to continue? (y/n): ")) ||
488 (ua->cmd[0] != 'y' && ua->cmd[0] != 'Y')) {
492 memset(&pr, 0, sizeof(pr));
493 if (!select_pool_dbr(ua, &pr)) {
496 memset(&omr, 0, sizeof(omr));
498 /* Fire off the label requests */
499 for (vl=vol_list; vl; vl=vl->next) {
500 if (!vl->VolName || !slot_list[vl->Slot]) {
503 memset(&mr, 0, sizeof(mr));
504 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
505 media_record_exists = false;
506 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
507 if (mr.VolBytes != 0) {
508 bsendmsg(ua, _("Media record for Slot %d Volume \"%s\" already exists.\n"),
509 vl->Slot, mr.VolumeName);
512 mr.StorageId = store->StorageId;
513 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
514 bsendmsg(ua, _("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
518 media_record_exists = true;
521 mr.StorageId = store->StorageId;
523 * Deal with creating cleaning tape here. Normal tapes created in
524 * send_label_request() below
526 if (is_cleaning_tape(ua, &mr, &pr)) {
527 if (media_record_exists) { /* we update it */
529 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
531 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
532 bsendmsg(ua, "%s", db_strerror(ua->db));
534 } else { /* create the media record */
535 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
536 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
538 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
539 bsendmsg(ua, _("Catalog record for cleaning tape \"%s\" successfully created.\n"),
541 pr.NumVols++; /* this is a bit suspect */
542 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
543 bsendmsg(ua, "%s", db_strerror(ua->db));
546 bsendmsg(ua, _("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
549 continue; /* done, go handle next volume */
551 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
554 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
560 free_vol_list(vol_list);
567 * Check if the Volume name has legal characters
568 * If ua is non-NULL send the message
570 bool is_volume_name_legal(UAContext *ua, const char *name)
574 const char *accept = ":.-_";
576 /* Restrict the characters permitted in the Volume name */
577 for (p=name; *p; p++) {
578 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
582 bsendmsg(ua, _("Illegal character \"%c\" in a volume name.\n"), *p);
587 if (len >= MAX_NAME_LENGTH) {
589 bsendmsg(ua, _("Volume name too long.\n"));
595 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
603 * NOTE! This routine opens the SD socket but leaves it open
605 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
606 POOL_DBR *pr, int relabel, bool media_record_exists,
610 char dev_name[MAX_NAME_LENGTH];
613 if (!(sd=open_sd_bsock(ua))) {
616 bstrncpy(dev_name, ua->jcr->store->dev_name(), sizeof(dev_name));
617 bash_spaces(dev_name);
618 bash_spaces(mr->VolumeName);
619 bash_spaces(mr->MediaType);
620 bash_spaces(pr->Name);
622 bash_spaces(omr->VolumeName);
623 bnet_fsend(sd, "relabel %s OldName=%s NewName=%s PoolName=%s "
624 "MediaType=%s Slot=%d drive=%d",
625 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
626 mr->MediaType, mr->Slot, drive);
627 bsendmsg(ua, _("Sending relabel command from \"%s\" to \"%s\" ...\n"),
628 omr->VolumeName, mr->VolumeName);
630 bnet_fsend(sd, "label %s VolumeName=%s PoolName=%s MediaType=%s "
632 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
634 bsendmsg(ua, _("Sending label command for Volume \"%s\" Slot %d ...\n"),
635 mr->VolumeName, mr->Slot);
636 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
637 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
640 while (bnet_recv(sd) >= 0) {
641 bsendmsg(ua, "%s", sd->msg);
642 if (strncmp(sd->msg, "3000 OK label.", 14) == 0) {
646 unbash_spaces(mr->VolumeName);
647 unbash_spaces(mr->MediaType);
648 unbash_spaces(pr->Name);
649 mr->LabelDate = time(NULL);
650 mr->set_label_date = true;
652 if (media_record_exists) { /* we update it */
655 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
656 bsendmsg(ua, "%s", db_strerror(ua->db));
659 } else { /* create the media record */
660 set_pool_dbr_defaults_in_media_dbr(mr, pr);
661 mr->VolBytes = 1; /* flag indicating Volume labeled */
663 if (db_create_media_record(ua->jcr, ua->db, mr)) {
664 bsendmsg(ua, _("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
665 mr->VolumeName, mr->Slot);
666 /* Update number of volumes in pool */
668 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
669 bsendmsg(ua, "%s", db_strerror(ua->db));
672 bsendmsg(ua, "%s", db_strerror(ua->db));
677 bsendmsg(ua, _("Label command failed for Volume %s.\n"), mr->VolumeName);
682 static BSOCK *open_sd_bsock(UAContext *ua)
684 STORE *store = ua->jcr->store;
686 if (!ua->jcr->store_bsock) {
687 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"),
688 store->hdr.name, store->address, store->SDport);
689 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
690 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
694 return ua->jcr->store_bsock;
697 static void close_sd_bsock(UAContext *ua)
699 if (ua->jcr->store_bsock) {
700 bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
701 bnet_close(ua->jcr->store_bsock);
702 ua->jcr->store_bsock = NULL;
706 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
708 STORE *store = ua->jcr->store;
710 char dev_name[MAX_NAME_LENGTH];
711 char *VolName = NULL;
714 if (!(sd=open_sd_bsock(ua))) {
715 bsendmsg(ua, _("Could not open SD socket.\n"));
718 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
719 bash_spaces(dev_name);
720 /* Ask for autochanger list of volumes */
721 bnet_fsend(sd, _("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
722 Dmsg1(100, "Sent: %s", sd->msg);
724 /* Get Volume name in this Slot */
725 while (bnet_recv(sd) >= 0) {
726 bsendmsg(ua, "%s", sd->msg);
727 Dmsg1(100, "Got: %s", sd->msg);
728 if (strncmp(sd->msg, "3001 Volume=", 12) == 0) {
729 VolName = (char *)malloc(sd->msglen);
730 if (sscanf(sd->msg, "3001 Volume=%s Slot=%d", VolName, &rtn_slot) == 2) {
738 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
743 * We get the slot list from the Storage daemon.
744 * If scan is set, we return all slots found,
745 * otherwise, we return only slots with valid barcodes (Volume names)
747 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
749 STORE *store = ua->jcr->store;
750 char dev_name[MAX_NAME_LENGTH];
753 vol_list_t *vol_list = NULL;
756 if (!(sd=open_sd_bsock(ua))) {
760 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
761 bash_spaces(dev_name);
762 /* Ask for autochanger list of volumes */
763 bnet_fsend(sd, _("autochanger list %s \n"), dev_name);
765 /* Read and organize list of Volumes */
766 while (bnet_recv(sd) >= 0) {
769 strip_trailing_junk(sd->msg);
771 /* Check for returned SD messages */
772 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
773 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
775 bsendmsg(ua, "%s\n", sd->msg); /* pass them on to user */
779 /* Validate Slot: if scanning, otherwise Slot:Barcode */
780 p = strchr(sd->msg, ':');
782 /* Scanning -- require only valid slot */
783 Slot = atoi(sd->msg);
787 bsendmsg(ua, _("Invalid Slot number: %s\n"), sd->msg);
792 if (p && strlen(p) > 1) {
794 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
797 bsendmsg(ua, _("Invalid Slot number: %s\n"), sd->msg);
803 if (!is_volume_name_legal(ua, p)) {
806 bsendmsg(ua, _("Invalid Volume name: %s\n"), sd->msg);
811 /* Add Slot and VolumeName to list */
812 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
816 p++; /* skip separator */
818 vl->VolName = bstrdup(p);
822 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
827 /* Add new entry to end of list */
828 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
841 static void free_vol_list(vol_list_t *vol_list)
846 for (vl=vol_list; vl; ) {
858 * We get the number of slots in the changer from the SD
860 static int get_num_slots_from_SD(UAContext *ua)
862 STORE *store = ua->jcr->store;
863 char dev_name[MAX_NAME_LENGTH];
868 if (!(sd=open_sd_bsock(ua))) {
872 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
873 bash_spaces(dev_name);
874 /* Ask for autochanger list of volumes */
875 bnet_fsend(sd, _("autochanger slots %s \n"), dev_name);
877 while (bnet_recv(sd) >= 0) {
878 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
881 bsendmsg(ua, "%s", sd->msg);
885 bsendmsg(ua, _("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
892 * Check if this is a cleaning tape by comparing the Volume name
893 * with the Cleaning Prefix. If they match, this is a cleaning
896 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
898 /* Find Pool resource */
899 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
900 if (!ua->jcr->pool) {
901 bsendmsg(ua, _("Pool \"%s\" resource not found!\n"), pr->Name);
904 if (ua->jcr->pool->cleaning_prefix == NULL) {
907 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
908 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
909 strlen(ua->jcr->pool->cleaning_prefix),
910 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
911 strlen(ua->jcr->pool->cleaning_prefix)));
912 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
913 strlen(ua->jcr->pool->cleaning_prefix)) == 0;