]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_label.c
Fix relabel command
[bacula/bacula] / bacula / src / dird / ua_label.c
1 /*
2  *
3  *   Bacula Director -- Tape labeling commands
4  *
5  *     Kern Sibbald, April MMIII
6  *
7  *   Version $Id$
8  */
9
10 /*
11    Copyright (C) 2000-2003 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "dird.h"
32
33 /* Slot list definition */
34 typedef struct s_vol_list {
35    struct s_vol_list *next;
36    char *VolName;
37    int Slot;
38 } vol_list_t;
39
40
41 /* Forward referenced functions */
42 static int do_label(UAContext *ua, char *cmd, int relabel);
43 static void label_from_barcodes(UAContext *ua);
44 static int send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 
45                POOL_DBR *pr, int relabel, bool media_record_exits);
46 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan);
47 static void free_vol_list(vol_list_t *vol_list);
48 static int is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr);
49 static BSOCK *open_sd_bsock(UAContext *ua);
50 static void close_sd_bsock(UAContext *ua);
51 static char *get_volume_name_from_SD(UAContext *ua, int Slot);
52
53
54 /*
55  * Label a tape 
56  *  
57  *   label storage=xxx volume=vvv
58  */
59 int label_cmd(UAContext *ua, char *cmd)
60 {
61    return do_label(ua, cmd, 0);       /* standard label */
62 }
63
64 int relabel_cmd(UAContext *ua, char *cmd)
65 {
66    return do_label(ua, cmd, 1);      /* relabel tape */
67 }
68
69 #define MAX_SLOTS 5000
70
71 static bool get_user_slot_list(UAContext *ua, char *slot_list, int num_slots)
72 {
73    int i;
74    char *msg;
75
76    for (int i=0; i<num_slots; i++) {
77       slot_list[i] = 0;
78    }
79    i = find_arg_with_value(ua, "slots");
80    if (i >= 0) {
81       /* scan slot list in ua->argv[i] */
82       char *p, *e, *h;
83       int beg, end;
84
85       strip_trailing_junk(ua->argv[i]);
86       for (p=ua->argv[i]; p && *p; p=e) {
87          /* Check for list */
88          e = strchr(p, ',');
89          if (e) {
90             *e++ = 0;
91          }
92          /* Check for range */
93          h = strchr(p, '-');             /* range? */
94          if (h == p) {
95             msg = _("Negative numbers not permitted\n");
96             goto bail_out;
97          }
98          if (h) {
99             *h++ = 0;
100             if (!is_an_integer(h)) {
101                msg = _("Range end is not integer.\n");
102                goto bail_out;
103             }
104             skip_spaces(&p);
105             if (!is_an_integer(p)) {
106                msg = _("Range start is not an integer.\n");
107                goto bail_out;
108             }
109             beg = atoi(p);
110             end = atoi(h);
111             if (end < beg) {
112                msg = _("Range end not bigger than start.\n");
113                goto bail_out;
114             }
115          } else {
116             skip_spaces(&p);
117             if (!is_an_integer(p)) {
118                msg = _("Input value is not an integer.\n");
119                goto bail_out;
120             }
121             beg = end = atoi(p);
122          }
123          if (beg <= 0 || end <= 0) {
124             msg = _("Values must be be greater than zero.\n");
125             goto bail_out;
126          }
127          if (end >= num_slots) {
128             msg = _("Slot too large.\n");
129             goto bail_out;
130          }
131          for (i=beg; i<=end; i++) {
132             slot_list[i] = 1;         /* Turn on specified range */
133          }
134       }
135    } else { 
136       /* Turn everything on */
137       for (i=0; i<num_slots; i++) {
138          slot_list[i] = 1;
139       }
140    }
141 #ifdef xxx_debug
142    printf("Slots turned on:\n");
143    for (i=1; i<num_slots; i++) {
144       if (slot_list[i]) {
145          printf("%d\n", i); 
146       }
147    }
148 #endif
149    return true;
150
151 bail_out:
152    return false;
153 }
154
155 /*
156  * Update Slots corresponding to Volumes in autochanger 
157  */
158 int update_slots(UAContext *ua)
159 {
160    STORE *store;
161    vol_list_t *vl, *vol_list = NULL;
162    MEDIA_DBR mr;
163    char *slot_list;
164    bool scan;        
165
166    if (!open_db(ua)) {
167       return 1;
168    }
169    store = get_storage_resource(ua, 1);
170    if (!store) {
171       return 1;
172    }
173    ua->jcr->store = store;
174
175    scan = find_arg(ua, _("scan")) >= 0;
176
177    slot_list = (char *)malloc(MAX_SLOTS);
178    if (!get_user_slot_list(ua, slot_list, MAX_SLOTS)) {
179       free(slot_list);
180       return 1;
181    }
182
183    vol_list = get_vol_list_from_SD(ua, scan);
184
185    if (!vol_list) {
186       bsendmsg(ua, _("No Volumes found to label, or no barcodes.\n"));
187       goto bail_out;
188    }
189
190    /* Walk through the list updating the media records */
191    for (vl=vol_list; vl; vl=vl->next) {
192       /* Check if user wants us to look at this slot */
193       if (!slot_list[vl->Slot]) {
194          continue;
195       }
196       /* If scanning, we read the label rather than the barcode */
197       if (scan) {
198          if (vl->VolName) {
199             free(vl->VolName);
200             vl->VolName = NULL;
201          }
202          vl->VolName = get_volume_name_from_SD(ua, vl->Slot);
203       }
204       if (!vl->VolName) {
205          continue;
206       }
207       memset(&mr, 0, sizeof(mr));
208       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
209       db_lock(ua->db);
210       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
211           if (mr.Slot != vl->Slot || !mr.InChanger) {
212              mr.Slot = vl->Slot;
213              mr.InChanger = 1;
214              if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
215                 bsendmsg(ua, _("%s\n"), db_strerror(ua->db));
216              } else {
217                 bsendmsg(ua, _(
218                   "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
219                   mr.VolumeName, mr.Slot);
220              }
221           } else {
222              bsendmsg(ua, _("Catalog record for Volume \"%s\" is up to date.\n"),
223                 mr.VolumeName);
224           }   
225           db_unlock(ua->db);
226           continue;
227       } else {
228           bsendmsg(ua, _("Record for Volume \"%s\" not found in catalog.\n"), 
229              mr.VolumeName);
230       }
231       db_unlock(ua->db);
232    }
233
234
235 bail_out:
236
237    free_vol_list(vol_list);
238    free(slot_list);
239    close_sd_bsock(ua);
240
241    return 1;
242 }
243
244
245 /*
246  * Common routine for both label and relabel
247  */
248 static int do_label(UAContext *ua, char *cmd, int relabel)
249 {
250    STORE *store;
251    BSOCK *sd;
252    char dev_name[MAX_NAME_LENGTH];
253    MEDIA_DBR mr, omr;
254    POOL_DBR pr;
255    bool print_reminder = true;
256    int ok = FALSE;
257    int i;
258    bool media_record_exists = false;
259    static char *barcode_keyword[] = {
260       "barcode",
261       "barcodes",
262       NULL};
263
264
265    memset(&pr, 0, sizeof(pr));
266    if (!open_db(ua)) {
267       return 1;
268    }
269    store = get_storage_resource(ua, 1);
270    if (!store) {
271       return 1;
272    }
273    ua->jcr->store = store;
274
275    if (!relabel && find_arg_keyword(ua, barcode_keyword) >= 0) {
276       label_from_barcodes(ua);
277       return 1;
278    }
279
280    /* If relabel get name of Volume to relabel */
281    if (relabel) {
282       /* Check for oldvolume=name */
283       i = find_arg_with_value(ua, "oldvolume"); 
284       if (i >= 0) {
285          memset(&omr, 0, sizeof(omr));
286          bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
287          if (db_get_media_record(ua->jcr, ua->db, &omr)) {
288             goto checkVol;
289          } 
290          bsendmsg(ua, "%s", db_strerror(ua->db));
291       }
292       /* No keyword or Vol not found, ask user to select */
293       if (!select_media_dbr(ua, &omr)) {
294          return 1;
295       }
296
297       /* Require Volume to be Purged or Recycled */
298 checkVol:
299       if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
300          bsendmsg(ua, _("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
301             omr.VolumeName, omr.VolStatus);
302          return 1;
303       }
304    }
305
306    /* Check for volume=NewVolume */
307    i = find_arg_with_value(ua, "volume");
308    if (i >= 0) {
309       pm_strcpy(&ua->cmd, ua->argv[i]);
310       goto checkName;
311    }
312
313    /* Get a new Volume name */
314    for ( ;; ) {
315       media_record_exists = false;
316       if (!get_cmd(ua, _("Enter new Volume name: "))) {
317          return 1;
318       }
319 checkName:
320       if (!is_volume_name_legal(ua, ua->cmd)) {
321          continue;
322       }
323
324       memset(&mr, 0, sizeof(mr));
325       bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
326       /* If VolBytes are zero the Volume is not labeled */
327       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
328          if (mr.VolBytes != 0) {
329              bsendmsg(ua, _("Media record for new Volume \"%s\" already exists.\n"), 
330                 mr.VolumeName);
331              continue;
332           }
333           media_record_exists = true;
334       }
335       break;                          /* Got it */
336    }
337
338    /* If autochanger, request slot */
339    if (store->autochanger) {
340       i = find_arg_with_value(ua, "slot"); 
341       if (i >= 0) {
342          mr.Slot = atoi(ua->argv[i]);
343       } else if (!get_pint(ua, _("Enter slot (0 for none): "))) {
344          return 1;
345       } else {
346          mr.Slot = ua->pint32_val;
347       }
348       mr.InChanger = 1;               /* assumed if we are labeling it */
349    }
350
351    bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
352
353    /* Must select Pool if not already done */
354    if (pr.PoolId == 0) {
355       memset(&pr, 0, sizeof(pr));
356       if (!select_pool_dbr(ua, &pr)) {
357          return 1;
358       }
359    }
360
361
362    ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists);
363
364    if (ok) {
365       sd = ua->jcr->store_bsock;
366       if (relabel) {
367          if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
368             bsendmsg(ua, _("Delete of Volume \"%s\" failed. ERR=%s"),
369                omr.VolumeName, db_strerror(ua->db));
370          } else {
371             bsendmsg(ua, _("Old volume \"%s\" deleted from catalog.\n"), 
372                omr.VolumeName);
373          }
374       }
375       if (ua->automount) {
376          bstrncpy(dev_name, store->dev_name, sizeof(dev_name));
377          bsendmsg(ua, _("Requesting to mount %s ...\n"), dev_name);
378          bash_spaces(dev_name);
379          bnet_fsend(sd, "mount %s", dev_name);
380          unbash_spaces(dev_name);
381          while (bnet_recv(sd) >= 0) {
382             bsendmsg(ua, "%s", sd->msg);
383             /* Here we can get
384              *  3001 OK mount. Device=xxx      or
385              *  3001 Mounted Volume vvvv
386              *  3906 is cannot mount non-tape
387              * So for those, no need to print a reminder
388              */
389             if (strncmp(sd->msg, "3001 ", 5) == 0 ||
390                 strncmp(sd->msg, "3906 ", 5) == 0) {
391                print_reminder = false;
392             }
393          }
394       }
395    }
396    if (print_reminder) {
397       bsendmsg(ua, _("Do not forget to mount the drive!!!\n"));
398    }
399    close_sd_bsock(ua);
400
401    return 1;
402 }
403
404 /*
405  * Request SD to send us the slot:barcodes, then wiffle
406  *  through them all labeling them.
407  */
408 static void label_from_barcodes(UAContext *ua)
409 {
410    STORE *store = ua->jcr->store;
411    POOL_DBR pr;
412    MEDIA_DBR mr, omr;
413    vol_list_t *vl, *vol_list = NULL;
414    bool media_record_exists;
415    char *slot_list;
416
417    slot_list = (char *)malloc(MAX_SLOTS);
418    if (!get_user_slot_list(ua, slot_list, MAX_SLOTS)) {
419       free(slot_list);
420       return;
421    }
422
423    vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
424
425    if (!vol_list) {
426       bsendmsg(ua, _("No Volumes found to label, or no barcodes.\n"));
427       goto bail_out;
428    }
429
430    /* Display list of Volumes and ask if he really wants to proceed */
431    bsendmsg(ua, _("The following Volumes will be labeled:\n"
432                   "Slot  Volume\n"
433                   "==============\n"));
434    for (vl=vol_list; vl; vl=vl->next) {
435       if (!vl->VolName || !slot_list[vl->Slot]) {
436          continue;
437       }
438       bsendmsg(ua, "%4d  %s\n", vl->Slot, vl->VolName);
439    }
440    if (!get_cmd(ua, _("Do you want to continue? (y/n): ")) ||
441        (ua->cmd[0] != 'y' && ua->cmd[0] != 'Y')) {
442       goto bail_out;
443    }
444    /* Select a pool */
445    memset(&pr, 0, sizeof(pr));
446    if (!select_pool_dbr(ua, &pr)) {
447       goto bail_out;
448    }
449    memset(&omr, 0, sizeof(omr));
450
451    /* Fire off the label requests */
452    for (vl=vol_list; vl; vl=vl->next) {
453       if (!vl->VolName || !slot_list[vl->Slot]) {
454          continue;
455       }
456       memset(&mr, 0, sizeof(mr));
457       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
458       media_record_exists = false;
459       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
460           if (mr.VolBytes != 0) {
461              bsendmsg(ua, _("Media record for Slot %d Volume \"%s\" already exists.\n"), 
462                 vl->Slot, mr.VolumeName);
463              if (!mr.InChanger) {
464                 mr.InChanger = 1;
465                 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
466                    bsendmsg(ua, "Error setting InChanger: ERR=%s", db_strerror(ua->db));
467                 }
468              }
469              continue;
470           } 
471           media_record_exists = true;
472       }
473       mr.InChanger = 1;
474       /*
475        * Deal with creating cleaning tape here. Normal tapes created in
476        *  send_label_request() below
477        */
478       if (is_cleaning_tape(ua, &mr, &pr)) {
479          if (media_record_exists) {      /* we update it */
480             mr.VolBytes = 1;
481             if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
482                 bsendmsg(ua, "%s", db_strerror(ua->db));
483             }
484          } else {                        /* create the media record */
485             set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
486             if (db_create_media_record(ua->jcr, ua->db, &mr)) {
487                bsendmsg(ua, _("Catalog record for cleaning tape \"%s\" successfully created.\n"),
488                   mr.VolumeName);
489             } else {
490                bsendmsg(ua, "Catalog error on cleaning tape: %s", db_strerror(ua->db));
491             }
492          }
493          continue;                    /* done, go handle next volume */
494       }
495       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
496
497       mr.Slot = vl->Slot;
498       if (!send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists)) {
499          goto bail_out;
500       }
501    }
502
503
504 bail_out:
505    free_vol_list(vol_list);
506    close_sd_bsock(ua);
507
508    return;
509 }
510
511 /* 
512  * Check if the Volume name has legal characters
513  * If ua is non-NULL send the message
514  */
515 int is_volume_name_legal(UAContext *ua, char *name)
516 {
517    int len;
518    char *p;
519    char *accept = ":.-_";
520
521    /* Restrict the characters permitted in the Volume name */
522    for (p=name; *p; p++) {
523       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
524          continue;
525       }
526       if (ua) {
527          bsendmsg(ua, _("Illegal character \"%c\" in a volume name.\n"), *p);
528       }
529       return 0;
530    }
531    len = strlen(name);
532    if (len >= MAX_NAME_LENGTH) {
533       if (ua) {
534          bsendmsg(ua, _("Volume name too long.\n"));
535       }
536       return 0;
537    }
538    if (len == 0) {
539       if (ua) {
540          bsendmsg(ua, _("Volume name must be at least one character long.\n"));
541       }
542       return 0;
543    }
544    return 1;
545 }
546
547 static int send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 
548                               POOL_DBR *pr, int relabel, bool media_record_exists)
549 {
550    BSOCK *sd;
551    char dev_name[MAX_NAME_LENGTH];
552    int ok = FALSE;
553
554    if (!(sd=open_sd_bsock(ua))) {
555       return 0;
556    }
557    bstrncpy(dev_name, ua->jcr->store->dev_name, sizeof(dev_name));
558    bash_spaces(dev_name);
559    bash_spaces(mr->VolumeName);
560    bash_spaces(mr->MediaType);
561    bash_spaces(pr->Name);
562    if (relabel) {
563       bash_spaces(omr->VolumeName);
564       bnet_fsend(sd, "relabel %s OldName=%s NewName=%s PoolName=%s MediaType=%s Slot=%d", 
565          dev_name, omr->VolumeName, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
566       bsendmsg(ua, _("Sending relabel command from \"%s\" to \"%s\" ...\n"),
567          omr->VolumeName, mr->VolumeName);
568    } else {
569       bnet_fsend(sd, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d", 
570          dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
571       bsendmsg(ua, _("Sending label command for Volume \"%s\" Slot %d ...\n"), 
572          mr->VolumeName, mr->Slot);
573       Dmsg5(200, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d\n", 
574          dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
575    }
576
577    while (bnet_recv(sd) >= 0) {
578       bsendmsg(ua, "%s", sd->msg);
579       if (strncmp(sd->msg, "3000 OK label.", 14) == 0) {
580          ok = TRUE;
581       } 
582    }
583    unbash_spaces(mr->VolumeName);
584    unbash_spaces(mr->MediaType);
585    unbash_spaces(pr->Name);
586    mr->LabelDate = time(NULL);
587    if (ok) {
588       if (media_record_exists) {      /* we update it */
589          mr->VolBytes = 1;
590          mr->InChanger = 1;
591          if (!db_update_media_record(ua->jcr, ua->db, mr)) {
592              bsendmsg(ua, "%s", db_strerror(ua->db));
593              ok = FALSE;
594          }
595       } else {                        /* create the media record */
596          set_pool_dbr_defaults_in_media_dbr(mr, pr);
597          mr->VolBytes = 1;               /* flag indicating Volume labeled */
598          mr->InChanger = 1;
599          if (db_create_media_record(ua->jcr, ua->db, mr)) {
600             bsendmsg(ua, _("Catalog record for Volume \"%s\", Slot %d  successfully created.\n"),
601             mr->VolumeName, mr->Slot);
602          } else {
603             bsendmsg(ua, "%s", db_strerror(ua->db));
604             ok = FALSE;
605          }
606       }
607    } else {
608       bsendmsg(ua, _("Label command failed.\n"));
609    }
610    return ok;
611 }
612
613 static BSOCK *open_sd_bsock(UAContext *ua) 
614 {
615    STORE *store = ua->jcr->store;
616
617    if (!ua->jcr->store_bsock) {
618       bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"), 
619          store->hdr.name, store->address, store->SDport);
620       if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
621          bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
622          return NULL;
623       }
624    }
625    return ua->jcr->store_bsock;
626 }
627
628 static void close_sd_bsock(UAContext *ua)
629 {
630    if (ua->jcr->store_bsock) {
631       bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
632       bnet_close(ua->jcr->store_bsock);
633       ua->jcr->store_bsock = NULL;
634    }
635 }
636
637 static char *get_volume_name_from_SD(UAContext *ua, int Slot) 
638 {
639    STORE *store = ua->jcr->store;
640    BSOCK *sd;
641    char dev_name[MAX_NAME_LENGTH];
642    char *VolName = NULL;
643    int rtn_slot;
644
645    if (!(sd=open_sd_bsock(ua))) {
646       return NULL;
647    }
648    bstrncpy(dev_name, store->dev_name, sizeof(dev_name));
649    bash_spaces(dev_name);
650    /* Ask for autochanger list of volumes */
651    bnet_fsend(sd, _("readlabel %s Slot=%d\n"), dev_name, Slot);
652    Dmsg1(100, "Sent: %s", sd->msg);
653
654    /* Get Volume name in this Slot */
655    while (bnet_recv(sd) >= 0) {
656       bsendmsg(ua, "%s", sd->msg);
657       if (strncmp(sd->msg, "3001 Volume=", 12) == 0) {
658          VolName = (char *)malloc(sd->msglen);
659          if (sscanf(sd->msg, "3001 Volume=%s Slot=%d", VolName, &rtn_slot) == 2) {
660             break;
661          }
662          free(VolName);
663          VolName = NULL;
664       }
665    }
666    Dmsg1(200, "get_vol_name=%s\n", NPRT(VolName));
667    return VolName;
668 }
669
670 /*
671  * We get the slot list from the Storage daemon.
672  *  If scan is set, we return all slots found,
673  *  otherwise, we return only slots with valid barcodes (Volume names)
674  */
675 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan) 
676 {
677    STORE *store = ua->jcr->store;
678    char dev_name[MAX_NAME_LENGTH];
679    BSOCK *sd;
680    vol_list_t *vl;
681    vol_list_t *vol_list = NULL;
682
683
684    if (!(sd=open_sd_bsock(ua))) {
685       return NULL;
686    }
687
688    bstrncpy(dev_name, store->dev_name, sizeof(dev_name));
689    bash_spaces(dev_name);
690    /* Ask for autochanger list of volumes */
691    bnet_fsend(sd, _("autochanger list %s \n"), dev_name);
692
693    /* Read and organize list of Volumes */
694    while (bnet_recv(sd) >= 0) {
695       char *p;
696       int Slot;
697       strip_trailing_junk(sd->msg);
698
699       /* Check for returned SD messages */
700       if (sd->msg[0] == '3'     && B_ISDIGIT(sd->msg[1]) &&
701           B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
702           sd->msg[4] == ' ') {
703          bsendmsg(ua, "%s\n", sd->msg);   /* pass them on to user */
704          continue;
705       }
706
707       /* Validate Slot: if scanning, otherwise  Slot:Barcode */
708       p = strchr(sd->msg, ':');
709       if (scan && p) {
710          /* Scanning -- require only valid slot */
711          Slot = atoi(sd->msg);
712          if (Slot <= 0) {
713             p--;
714             *p = ':';
715             bsendmsg(ua, _("Invalid Slot number: %s\n"), sd->msg); 
716             continue;
717          }
718       } else {
719          /* Not scanning */
720          if (p && strlen(p) > 1) {
721             *p++ = 0;
722             if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
723                p--;
724                *p = ':';
725                bsendmsg(ua, _("Invalid Slot number: %s\n"), sd->msg); 
726                continue;
727             }
728          } else {
729             continue;
730          }
731          if (!is_volume_name_legal(ua, p)) {
732             p--;
733             *p = ':';
734             bsendmsg(ua, _("Invalid Volume name: %s\n"), sd->msg); 
735             continue;
736          }
737       }
738
739       /* Add Slot and VolumeName to list */
740       vl = (vol_list_t *)malloc(sizeof(vol_list_t));
741       vl->Slot = Slot;
742       if (p) {
743          vl->VolName = bstrdup(p);
744       } else {
745          vl->VolName = NULL;
746       }
747       if (!vol_list) {
748          vl->next = vol_list;
749          vol_list = vl;
750       } else {
751          /* Add new entry to end of list */
752          for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
753             if (!tvl->next) {
754                tvl->next = vl;
755                vl->next = NULL;
756                break;
757             }
758          }
759       }
760    }
761    close_sd_bsock(ua);
762    return vol_list;
763 }
764
765 static void free_vol_list(vol_list_t *vol_list)
766 {
767    vol_list_t *vl;
768    /* Free list */
769    for (vl=vol_list; vl; ) {
770       vol_list_t *ovl;
771       if (vl->VolName) {
772          free(vl->VolName);
773       }
774       ovl = vl;
775       vl = vl->next;
776       free(ovl);
777    }
778 }
779
780
781 /*
782  * Check if this is a cleaning tape by comparing the Volume name
783  *  with the Cleaning Prefix. If they match, this is a cleaning 
784  *  tape.
785  */
786 static int is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
787 {
788    if (!ua->jcr->pool) {
789       /* Find Pool resource */
790       ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
791       if (!ua->jcr->pool) {
792          bsendmsg(ua, _("Pool %s resource not found!\n"), pr->Name);
793          return 1;
794       }
795    }
796    if (ua->jcr->pool->cleaning_prefix == NULL) {
797       return 0;
798    }
799    Dmsg4(200, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
800       ua->jcr->pool->cleaning_prefix, mr->VolumeName,
801       strlen(ua->jcr->pool->cleaning_prefix), 
802       strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
803                   strlen(ua->jcr->pool->cleaning_prefix)));
804    return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
805                   strlen(ua->jcr->pool->cleaning_prefix)) == 0;
806 }