3 * Bacula Director -- Tape labeling commands
5 * Kern Sibbald, April MMIII
10 Copyright (C) 2003-2006 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 /* slots are numbered 1 to num_slots */
70 for (int i=0; i <= num_slots; i++) {
73 i = find_arg_with_value(ua, "slots");
75 /* scan slot list in ua->argv[i] */
79 strip_trailing_junk(ua->argv[i]);
80 for (p=ua->argv[i]; p && *p; p=e) {
87 h = strchr(p, '-'); /* range? */
89 msg = _("Negative numbers not permitted\n");
94 if (!is_an_integer(h)) {
95 msg = _("Range end is not integer.\n");
99 if (!is_an_integer(p)) {
100 msg = _("Range start is not an integer.\n");
106 msg = _("Range end not bigger than start.\n");
111 if (!is_an_integer(p)) {
112 msg = _("Input value is not an integer.\n");
117 if (beg <= 0 || end <= 0) {
118 msg = _("Values must be be greater than zero.\n");
121 if (end > num_slots) {
122 msg = _("Slot too large.\n");
125 for (i=beg; i<=end; i++) {
126 slot_list[i] = 1; /* Turn on specified range */
130 /* Turn everything on */
131 for (i=1; i <= num_slots; i++) {
135 Dmsg0(100, "Slots turned on:\n");
136 for (i=1; i <= num_slots; i++) {
138 Dmsg1(100, "%d\n", i);
148 * Update Slots corresponding to Volumes in autochanger
150 int update_slots(UAContext *ua)
153 vol_list_t *vl, *vol_list = NULL;
164 store = get_storage_resource(ua, true/*arg is storage*/);
168 set_storage(ua->jcr, store);
169 drive = get_storage_drive(ua, store);
171 scan = find_arg(ua, N_("scan")) >= 0;
173 max_slots = get_num_slots_from_SD(ua);
174 Dmsg1(100, "max_slots=%d\n", max_slots);
175 if (max_slots <= 0) {
176 bsendmsg(ua, _("No slots in changer to scan.\n"));
179 slot_list = (char *)malloc(max_slots+1);
180 if (!get_user_slot_list(ua, slot_list, max_slots)) {
185 vol_list = get_vol_list_from_SD(ua, scan);
188 bsendmsg(ua, _("No Volumes found to label, or no barcodes.\n"));
192 /* First zap out any InChanger with StorageId=0 */
193 db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);
195 /* Walk through the list updating the media records */
196 for (vl=vol_list; vl; vl=vl->next) {
197 if (vl->Slot > max_slots) {
198 bsendmsg(ua, _("Slot %d greater than max %d ignored.\n"),
199 vl->Slot, max_slots);
202 /* Check if user wants us to look at this slot */
203 if (!slot_list[vl->Slot]) {
204 Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
207 /* If scanning, we read the label rather than the barcode */
213 vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
214 Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
216 slot_list[vl->Slot] = 0; /* clear Slot */
217 memset(&mr, 0, sizeof(mr));
220 mr.StorageId = store->StorageId;
221 /* Set InChanger to zero for this Slot */
223 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
226 Dmsg1(000, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
227 bsendmsg(ua, _("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
230 memset(&mr, 0, sizeof(mr));
231 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
233 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
234 if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store->StorageId) {
237 mr.StorageId = store->StorageId;
238 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
239 bsendmsg(ua, "%s", db_strerror(ua->db));
242 "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
243 mr.VolumeName, mr.Slot);
246 bsendmsg(ua, _("Catalog record for Volume \"%s\" is up to date.\n"),
252 bsendmsg(ua, _("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
253 mr.VolumeName, vl->Slot);
257 memset(&mr, 0, sizeof(mr));
259 mr.StorageId = store->StorageId;
261 for (int i=1; i <= max_slots; i++) {
264 /* Set InChanger to zero for this Slot */
265 db_make_inchanger_unique(ua->jcr, ua->db, &mr);
272 free_vol_list(vol_list);
281 * Common routine for both label and relabel
283 static int do_label(UAContext *ua, const char *cmd, int relabel)
287 char dev_name[MAX_NAME_LENGTH];
290 bool print_reminder = true;
291 bool label_barcodes = false;
295 bool media_record_exists = false;
296 static const char *barcode_keyword[] = {
302 memset(&pr, 0, sizeof(pr));
307 if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
308 *ua->argk[i] = 0; /* zap barcode keyword */
309 label_barcodes = true;
312 store = get_storage_resource(ua, true/*use default*/);
316 set_storage(ua->jcr, store);
317 drive = get_storage_drive(ua, store);
319 if (label_barcodes) {
320 label_from_barcodes(ua, drive);
324 /* If relabel get name of Volume to relabel */
326 /* Check for oldvolume=name */
327 i = find_arg_with_value(ua, "oldvolume");
329 memset(&omr, 0, sizeof(omr));
330 bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
331 if (db_get_media_record(ua->jcr, ua->db, &omr)) {
334 bsendmsg(ua, "%s", db_strerror(ua->db));
336 /* No keyword or Vol not found, ask user to select */
337 if (!select_media_dbr(ua, &omr)) {
341 /* Require Volume to be Purged or Recycled */
343 if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
344 bsendmsg(ua, _("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
345 omr.VolumeName, omr.VolStatus);
350 /* Check for volume=NewVolume */
351 i = find_arg_with_value(ua, "volume");
353 pm_strcpy(ua->cmd, ua->argv[i]);
357 /* Get a new Volume name */
359 media_record_exists = false;
360 if (!get_cmd(ua, _("Enter new Volume name: "))) {
364 if (!is_volume_name_legal(ua, ua->cmd)) {
368 memset(&mr, 0, sizeof(mr));
369 bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
370 /* If VolBytes are zero the Volume is not labeled */
371 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
372 if (mr.VolBytes != 0) {
373 bsendmsg(ua, _("Media record for new Volume \"%s\" already exists.\n"),
377 media_record_exists = true;
382 /* If autochanger, request slot */
383 i = find_arg_with_value(ua, "slot");
385 mr.Slot = atoi(ua->argv[i]);
386 mr.InChanger = 1; /* assumed if we are labeling it */
387 } else if (store->autochanger) {
388 if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
391 mr.Slot = ua->pint32_val;
392 mr.InChanger = 1; /* assumed if we are labeling it */
394 mr.StorageId = store->StorageId;
396 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
398 /* Must select Pool if not already done */
399 if (pr.PoolId == 0) {
400 memset(&pr, 0, sizeof(pr));
401 if (!select_pool_dbr(ua, &pr)) {
406 ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
409 sd = ua->jcr->store_bsock;
411 /* Delete the old media record */
412 if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
413 bsendmsg(ua, _("Delete of Volume \"%s\" failed. ERR=%s"),
414 omr.VolumeName, db_strerror(ua->db));
416 bsendmsg(ua, _("Old volume \"%s\" deleted from catalog.\n"),
418 /* Update the number of Volumes in the pool */
420 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
421 bsendmsg(ua, "%s", db_strerror(ua->db));
426 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
427 bsendmsg(ua, _("Requesting to mount %s ...\n"), dev_name);
428 bash_spaces(dev_name);
429 bnet_fsend(sd, "mount %s drive=%d", dev_name, drive);
430 unbash_spaces(dev_name);
431 while (bnet_recv(sd) >= 0) {
432 bsendmsg(ua, "%s", sd->msg);
434 * 3001 OK mount. Device=xxx or
435 * 3001 Mounted Volume vvvv
436 * 3002 Device "DVD-Writer" (/dev/hdc) is mounted.
437 * 3906 is cannot mount non-tape
438 * So for those, no need to print a reminder
440 if (strncmp(sd->msg, "3001 ", 5) == 0 ||
441 strncmp(sd->msg, "3002 ", 5) == 0 ||
442 strncmp(sd->msg, "3906 ", 5) == 0) {
443 print_reminder = false;
448 if (print_reminder) {
449 bsendmsg(ua, _("Do not forget to mount the drive!!!\n"));
457 * Request SD to send us the slot:barcodes, then wiffle
458 * through them all labeling them.
460 static void label_from_barcodes(UAContext *ua, int drive)
462 STORE *store = ua->jcr->store;
465 vol_list_t *vl, *vol_list = NULL;
466 bool media_record_exists;
471 max_slots = get_num_slots_from_SD(ua);
472 if (max_slots <= 0) {
473 bsendmsg(ua, _("No slots in changer to scan.\n"));
476 slot_list = (char *)malloc(max_slots+1);
477 if (!get_user_slot_list(ua, slot_list, max_slots)) {
481 vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
484 bsendmsg(ua, _("No Volumes found to label, or no barcodes.\n"));
488 /* Display list of Volumes and ask if he really wants to proceed */
489 bsendmsg(ua, _("The following Volumes will be labeled:\n"
491 "==============\n"));
492 for (vl=vol_list; vl; vl=vl->next) {
493 if (!vl->VolName || !slot_list[vl->Slot]) {
496 bsendmsg(ua, "%4d %s\n", vl->Slot, vl->VolName);
498 if (!get_cmd(ua, _("Do you want to continue? (y/n): ")) ||
499 (ua->cmd[0] != 'y' && ua->cmd[0] != 'Y')) {
503 memset(&pr, 0, sizeof(pr));
504 if (!select_pool_dbr(ua, &pr)) {
507 memset(&omr, 0, sizeof(omr));
509 /* Fire off the label requests */
510 for (vl=vol_list; vl; vl=vl->next) {
511 if (!vl->VolName || !slot_list[vl->Slot]) {
514 memset(&mr, 0, sizeof(mr));
515 bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
516 media_record_exists = false;
517 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
518 if (mr.VolBytes != 0) {
519 bsendmsg(ua, _("Media record for Slot %d Volume \"%s\" already exists.\n"),
520 vl->Slot, mr.VolumeName);
523 mr.StorageId = store->StorageId;
524 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
525 bsendmsg(ua, _("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
529 media_record_exists = true;
532 mr.StorageId = store->StorageId;
534 * Deal with creating cleaning tape here. Normal tapes created in
535 * send_label_request() below
537 if (is_cleaning_tape(ua, &mr, &pr)) {
538 if (media_record_exists) { /* we update it */
540 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
542 mr.StorageId = store->StorageId;
543 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
544 bsendmsg(ua, "%s", db_strerror(ua->db));
546 } else { /* create the media record */
547 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
548 bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
550 if (db_create_media_record(ua->jcr, ua->db, &mr)) {
551 bsendmsg(ua, _("Catalog record for cleaning tape \"%s\" successfully created.\n"),
553 pr.NumVols++; /* this is a bit suspect */
554 if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
555 bsendmsg(ua, "%s", db_strerror(ua->db));
558 bsendmsg(ua, _("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
561 continue; /* done, go handle next volume */
563 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
566 send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
572 free_vol_list(vol_list);
579 * Check if the Volume name has legal characters
580 * If ua is non-NULL send the message
582 bool is_volume_name_legal(UAContext *ua, const char *name)
586 const char *accept = ":.-_";
588 /* Restrict the characters permitted in the Volume name */
589 for (p=name; *p; p++) {
590 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
594 bsendmsg(ua, _("Illegal character \"%c\" in a volume name.\n"), *p);
599 if (len >= MAX_NAME_LENGTH) {
601 bsendmsg(ua, _("Volume name too long.\n"));
607 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
615 * NOTE! This routine opens the SD socket but leaves it open
617 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
618 POOL_DBR *pr, int relabel, bool media_record_exists,
622 char dev_name[MAX_NAME_LENGTH];
625 if (!(sd=open_sd_bsock(ua))) {
628 bstrncpy(dev_name, ua->jcr->store->dev_name(), sizeof(dev_name));
629 bash_spaces(dev_name);
630 bash_spaces(mr->VolumeName);
631 bash_spaces(mr->MediaType);
632 bash_spaces(pr->Name);
634 bash_spaces(omr->VolumeName);
635 bnet_fsend(sd, "relabel %s OldName=%s NewName=%s PoolName=%s "
636 "MediaType=%s Slot=%d drive=%d",
637 dev_name, omr->VolumeName, mr->VolumeName, pr->Name,
638 mr->MediaType, mr->Slot, drive);
639 bsendmsg(ua, _("Sending relabel command from \"%s\" to \"%s\" ...\n"),
640 omr->VolumeName, mr->VolumeName);
642 bnet_fsend(sd, "label %s VolumeName=%s PoolName=%s MediaType=%s "
644 dev_name, mr->VolumeName, pr->Name, mr->MediaType,
646 bsendmsg(ua, _("Sending label command for Volume \"%s\" Slot %d ...\n"),
647 mr->VolumeName, mr->Slot);
648 Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
649 dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
652 while (bnet_recv(sd) >= 0) {
653 bsendmsg(ua, "%s", sd->msg);
654 if (strncmp(sd->msg, "3000 OK label.", 14) == 0) {
658 unbash_spaces(mr->VolumeName);
659 unbash_spaces(mr->MediaType);
660 unbash_spaces(pr->Name);
661 mr->LabelDate = time(NULL);
662 mr->set_label_date = true;
664 if (media_record_exists) { /* we update it */
667 mr->StorageId = ua->jcr->store->StorageId;
668 if (!db_update_media_record(ua->jcr, ua->db, mr)) {
669 bsendmsg(ua, "%s", db_strerror(ua->db));
672 } else { /* create the media record */
673 set_pool_dbr_defaults_in_media_dbr(mr, pr);
674 mr->VolBytes = 1; /* flag indicating Volume labeled */
676 mr->StorageId = ua->jcr->store->StorageId;
677 if (db_create_media_record(ua->jcr, ua->db, mr)) {
678 bsendmsg(ua, _("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
679 mr->VolumeName, mr->Slot);
680 /* Update number of volumes in pool */
682 if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
683 bsendmsg(ua, "%s", db_strerror(ua->db));
686 bsendmsg(ua, "%s", db_strerror(ua->db));
691 bsendmsg(ua, _("Label command failed for Volume %s.\n"), mr->VolumeName);
696 static BSOCK *open_sd_bsock(UAContext *ua)
698 STORE *store = ua->jcr->store;
700 if (!ua->jcr->store_bsock) {
701 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"),
702 store->hdr.name, store->address, store->SDport);
703 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
704 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
708 return ua->jcr->store_bsock;
711 static void close_sd_bsock(UAContext *ua)
713 if (ua->jcr->store_bsock) {
714 bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
715 bnet_close(ua->jcr->store_bsock);
716 ua->jcr->store_bsock = NULL;
720 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
722 STORE *store = ua->jcr->store;
724 char dev_name[MAX_NAME_LENGTH];
725 char *VolName = NULL;
728 if (!(sd=open_sd_bsock(ua))) {
729 bsendmsg(ua, _("Could not open SD socket.\n"));
732 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
733 bash_spaces(dev_name);
734 /* Ask for autochanger list of volumes */
735 bnet_fsend(sd, _("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
736 Dmsg1(100, "Sent: %s", sd->msg);
738 /* Get Volume name in this Slot */
739 while (bnet_recv(sd) >= 0) {
740 bsendmsg(ua, "%s", sd->msg);
741 Dmsg1(100, "Got: %s", sd->msg);
742 if (strncmp(sd->msg, "3001 Volume=", 12) == 0) {
743 VolName = (char *)malloc(sd->msglen);
744 if (sscanf(sd->msg, "3001 Volume=%s Slot=%d", VolName, &rtn_slot) == 2) {
752 Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
757 * We get the slot list from the Storage daemon.
758 * If scan is set, we return all slots found,
759 * otherwise, we return only slots with valid barcodes (Volume names)
761 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
763 STORE *store = ua->jcr->store;
764 char dev_name[MAX_NAME_LENGTH];
767 vol_list_t *vol_list = NULL;
770 if (!(sd=open_sd_bsock(ua))) {
774 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
775 bash_spaces(dev_name);
776 /* Ask for autochanger list of volumes */
777 bnet_fsend(sd, _("autochanger list %s \n"), dev_name);
779 /* Read and organize list of Volumes */
780 while (bnet_recv(sd) >= 0) {
783 strip_trailing_junk(sd->msg);
785 /* Check for returned SD messages */
786 if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) &&
787 B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
789 bsendmsg(ua, "%s\n", sd->msg); /* pass them on to user */
793 /* Validate Slot: if scanning, otherwise Slot:Barcode */
794 p = strchr(sd->msg, ':');
796 /* Scanning -- require only valid slot */
797 Slot = atoi(sd->msg);
801 bsendmsg(ua, _("Invalid Slot number: %s\n"), sd->msg);
806 if (p && strlen(p) > 1) {
808 if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
811 bsendmsg(ua, _("Invalid Slot number: %s\n"), sd->msg);
817 if (!is_volume_name_legal(ua, p)) {
820 bsendmsg(ua, _("Invalid Volume name: %s\n"), sd->msg);
825 /* Add Slot and VolumeName to list */
826 vl = (vol_list_t *)malloc(sizeof(vol_list_t));
830 p++; /* skip separator */
832 vl->VolName = bstrdup(p);
836 Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
841 /* Add new entry to end of list */
842 for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
855 static void free_vol_list(vol_list_t *vol_list)
860 for (vl=vol_list; vl; ) {
872 * We get the number of slots in the changer from the SD
874 static int get_num_slots_from_SD(UAContext *ua)
876 STORE *store = ua->jcr->store;
877 char dev_name[MAX_NAME_LENGTH];
882 if (!(sd=open_sd_bsock(ua))) {
886 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
887 bash_spaces(dev_name);
888 /* Ask for autochanger number of slots */
889 bnet_fsend(sd, _("autochanger slots %s\n"), dev_name);
891 while (bnet_recv(sd) >= 0) {
892 if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
895 bsendmsg(ua, "%s", sd->msg);
899 bsendmsg(ua, _("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
904 * We get the number of drives in the changer from the SD
906 int get_num_drives_from_SD(UAContext *ua)
908 STORE *store = ua->jcr->store;
909 char dev_name[MAX_NAME_LENGTH];
914 if (!(sd=open_sd_bsock(ua))) {
918 bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
919 bash_spaces(dev_name);
920 /* Ask for autochanger number of slots */
921 bnet_fsend(sd, _("autochanger drives %s\n"), dev_name);
923 while (bnet_recv(sd) >= 0) {
924 if (sscanf(sd->msg, "drives=%d\n", &drives) == 1) {
927 bsendmsg(ua, "%s", sd->msg);
931 // bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
939 * Check if this is a cleaning tape by comparing the Volume name
940 * with the Cleaning Prefix. If they match, this is a cleaning
943 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
945 /* Find Pool resource */
946 ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
947 if (!ua->jcr->pool) {
948 bsendmsg(ua, _("Pool \"%s\" resource not found!\n"), pr->Name);
951 if (ua->jcr->pool->cleaning_prefix == NULL) {
954 Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
955 ua->jcr->pool->cleaning_prefix, mr->VolumeName,
956 strlen(ua->jcr->pool->cleaning_prefix),
957 strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
958 strlen(ua->jcr->pool->cleaning_prefix)));
959 return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
960 strlen(ua->jcr->pool->cleaning_prefix)) == 0;