]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_label.c
Misc update slots fixes
[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-2004 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 bool 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 static int const 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          Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
195          continue;
196       }
197       /* If scanning, we read the label rather than the barcode */
198       if (scan) {
199          if (vl->VolName) {
200             free(vl->VolName);
201             vl->VolName = NULL;
202          }
203          vl->VolName = get_volume_name_from_SD(ua, vl->Slot);
204          Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
205       }
206       if (!vl->VolName) {
207          Dmsg1(100, "No VolName for Slot=%d skipping.\n", vl->Slot);
208          continue;
209       }
210       memset(&mr, 0, sizeof(mr));
211       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
212       db_lock(ua->db);
213       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
214           if (mr.Slot != vl->Slot || !mr.InChanger) {
215              mr.Slot = vl->Slot;
216              mr.InChanger = 1;
217              if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
218                 bsendmsg(ua, _("%s\n"), db_strerror(ua->db));
219              } else {
220                 bsendmsg(ua, _(
221                   "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
222                   mr.VolumeName, mr.Slot);
223              }
224           } else {
225              bsendmsg(ua, _("Catalog record for Volume \"%s\" is up to date.\n"),
226                 mr.VolumeName);
227           }   
228           db_unlock(ua->db);
229           continue;
230       } else {
231           bsendmsg(ua, _("Record for Volume \"%s\" not found in catalog.\n"), 
232              mr.VolumeName);
233       }
234       db_unlock(ua->db);
235    }
236
237
238 bail_out:
239
240    free_vol_list(vol_list);
241    free(slot_list);
242    close_sd_bsock(ua);
243
244    return 1;
245 }
246
247
248 /*
249  * Common routine for both label and relabel
250  */
251 static int do_label(UAContext *ua, char *cmd, int relabel)
252 {
253    STORE *store;
254    BSOCK *sd;
255    char dev_name[MAX_NAME_LENGTH];
256    MEDIA_DBR mr, omr;
257    POOL_DBR pr;
258    bool print_reminder = true;
259    int ok = FALSE;
260    int i;
261    bool media_record_exists = false;
262    static char *barcode_keyword[] = {
263       "barcode",
264       "barcodes",
265       NULL};
266
267
268    memset(&pr, 0, sizeof(pr));
269    if (!open_db(ua)) {
270       return 1;
271    }
272    store = get_storage_resource(ua, 1);
273    if (!store) {
274       return 1;
275    }
276    ua->jcr->store = store;
277
278    if (!relabel && find_arg_keyword(ua, barcode_keyword) >= 0) {
279       label_from_barcodes(ua);
280       return 1;
281    }
282
283    /* If relabel get name of Volume to relabel */
284    if (relabel) {
285       /* Check for oldvolume=name */
286       i = find_arg_with_value(ua, "oldvolume"); 
287       if (i >= 0) {
288          memset(&omr, 0, sizeof(omr));
289          bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
290          if (db_get_media_record(ua->jcr, ua->db, &omr)) {
291             goto checkVol;
292          } 
293          bsendmsg(ua, "%s", db_strerror(ua->db));
294       }
295       /* No keyword or Vol not found, ask user to select */
296       if (!select_media_dbr(ua, &omr)) {
297          return 1;
298       }
299
300       /* Require Volume to be Purged or Recycled */
301 checkVol:
302       if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
303          bsendmsg(ua, _("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
304             omr.VolumeName, omr.VolStatus);
305          return 1;
306       }
307    }
308
309    /* Check for volume=NewVolume */
310    i = find_arg_with_value(ua, "volume");
311    if (i >= 0) {
312       pm_strcpy(&ua->cmd, ua->argv[i]);
313       goto checkName;
314    }
315
316    /* Get a new Volume name */
317    for ( ;; ) {
318       media_record_exists = false;
319       if (!get_cmd(ua, _("Enter new Volume name: "))) {
320          return 1;
321       }
322 checkName:
323       if (!is_volume_name_legal(ua, ua->cmd)) {
324          continue;
325       }
326
327       memset(&mr, 0, sizeof(mr));
328       bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
329       /* If VolBytes are zero the Volume is not labeled */
330       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
331          if (mr.VolBytes != 0) {
332              bsendmsg(ua, _("Media record for new Volume \"%s\" already exists.\n"), 
333                 mr.VolumeName);
334              continue;
335           }
336           media_record_exists = true;
337       }
338       break;                          /* Got it */
339    }
340
341    /* If autochanger, request slot */
342    if (store->autochanger) {
343       i = find_arg_with_value(ua, "slot"); 
344       if (i >= 0) {
345          mr.Slot = atoi(ua->argv[i]);
346       } else if (!get_pint(ua, _("Enter slot (0 for none): "))) {
347          return 1;
348       } else {
349          mr.Slot = ua->pint32_val;
350       }
351       mr.InChanger = 1;               /* assumed if we are labeling it */
352    }
353
354    bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
355
356    /* Must select Pool if not already done */
357    if (pr.PoolId == 0) {
358       memset(&pr, 0, sizeof(pr));
359       if (!select_pool_dbr(ua, &pr)) {
360          return 1;
361       }
362    }
363
364
365    ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists);
366
367    if (ok) {
368       sd = ua->jcr->store_bsock;
369       if (relabel) {
370          if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
371             bsendmsg(ua, _("Delete of Volume \"%s\" failed. ERR=%s"),
372                omr.VolumeName, db_strerror(ua->db));
373          } else {
374             bsendmsg(ua, _("Old volume \"%s\" deleted from catalog.\n"), 
375                omr.VolumeName);
376          }
377       }
378       if (ua->automount) {
379          bstrncpy(dev_name, store->dev_name, sizeof(dev_name));
380          bsendmsg(ua, _("Requesting to mount %s ...\n"), dev_name);
381          bash_spaces(dev_name);
382          bnet_fsend(sd, "mount %s", dev_name);
383          unbash_spaces(dev_name);
384          while (bnet_recv(sd) >= 0) {
385             bsendmsg(ua, "%s", sd->msg);
386             /* Here we can get
387              *  3001 OK mount. Device=xxx      or
388              *  3001 Mounted Volume vvvv
389              *  3906 is cannot mount non-tape
390              * So for those, no need to print a reminder
391              */
392             if (strncmp(sd->msg, "3001 ", 5) == 0 ||
393                 strncmp(sd->msg, "3906 ", 5) == 0) {
394                print_reminder = false;
395             }
396          }
397       }
398    }
399    if (print_reminder) {
400       bsendmsg(ua, _("Do not forget to mount the drive!!!\n"));
401    }
402    close_sd_bsock(ua);
403
404    return 1;
405 }
406
407 /*
408  * Request SD to send us the slot:barcodes, then wiffle
409  *  through them all labeling them.
410  */
411 static void label_from_barcodes(UAContext *ua)
412 {
413    STORE *store = ua->jcr->store;
414    POOL_DBR pr;
415    MEDIA_DBR mr, omr;
416    vol_list_t *vl, *vol_list = NULL;
417    bool media_record_exists;
418    char *slot_list;
419
420    slot_list = (char *)malloc(max_slots);
421    if (!get_user_slot_list(ua, slot_list, max_slots)) {
422       free(slot_list);
423       return;
424    }
425
426    vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
427
428    if (!vol_list) {
429       bsendmsg(ua, _("No Volumes found to label, or no barcodes.\n"));
430       goto bail_out;
431    }
432
433    /* Display list of Volumes and ask if he really wants to proceed */
434    bsendmsg(ua, _("The following Volumes will be labeled:\n"
435                   "Slot  Volume\n"
436                   "==============\n"));
437    for (vl=vol_list; vl; vl=vl->next) {
438       if (!vl->VolName || !slot_list[vl->Slot]) {
439          continue;
440       }
441       bsendmsg(ua, "%4d  %s\n", vl->Slot, vl->VolName);
442    }
443    if (!get_cmd(ua, _("Do you want to continue? (y/n): ")) ||
444        (ua->cmd[0] != 'y' && ua->cmd[0] != 'Y')) {
445       goto bail_out;
446    }
447    /* Select a pool */
448    memset(&pr, 0, sizeof(pr));
449    if (!select_pool_dbr(ua, &pr)) {
450       goto bail_out;
451    }
452    memset(&omr, 0, sizeof(omr));
453
454    /* Fire off the label requests */
455    for (vl=vol_list; vl; vl=vl->next) {
456       if (!vl->VolName || !slot_list[vl->Slot]) {
457          continue;
458       }
459       memset(&mr, 0, sizeof(mr));
460       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
461       media_record_exists = false;
462       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
463           if (mr.VolBytes != 0) {
464              bsendmsg(ua, _("Media record for Slot %d Volume \"%s\" already exists.\n"), 
465                 vl->Slot, mr.VolumeName);
466              if (!mr.InChanger) {
467                 mr.InChanger = 1;
468                 if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
469                    bsendmsg(ua, "Error setting InChanger: ERR=%s", db_strerror(ua->db));
470                 }
471              }
472              continue;
473           } 
474           media_record_exists = true;
475       }
476       mr.InChanger = 1;
477       /*
478        * Deal with creating cleaning tape here. Normal tapes created in
479        *  send_label_request() below
480        */
481       if (is_cleaning_tape(ua, &mr, &pr)) {
482          if (media_record_exists) {      /* we update it */
483             mr.VolBytes = 1;
484             if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
485                 bsendmsg(ua, "%s", db_strerror(ua->db));
486             }
487          } else {                        /* create the media record */
488             set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
489             if (db_create_media_record(ua->jcr, ua->db, &mr)) {
490                bsendmsg(ua, _("Catalog record for cleaning tape \"%s\" successfully created.\n"),
491                   mr.VolumeName);
492             } else {
493                bsendmsg(ua, "Catalog error on cleaning tape: %s", db_strerror(ua->db));
494             }
495          }
496          continue;                    /* done, go handle next volume */
497       }
498       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
499
500       mr.Slot = vl->Slot;
501       if (!send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists)) {
502          goto bail_out;
503       }
504    }
505
506
507 bail_out:
508    free_vol_list(vol_list);
509    close_sd_bsock(ua);
510
511    return;
512 }
513
514 /* 
515  * Check if the Volume name has legal characters
516  * If ua is non-NULL send the message
517  */
518 bool is_volume_name_legal(UAContext *ua, char *name)
519 {
520    int len;
521    char *p;
522    char *accept = ":.-_";
523
524    /* Restrict the characters permitted in the Volume name */
525    for (p=name; *p; p++) {
526       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
527          continue;
528       }
529       if (ua) {
530          bsendmsg(ua, _("Illegal character \"%c\" in a volume name.\n"), *p);
531       }
532       return 0;
533    }
534    len = strlen(name);
535    if (len >= MAX_NAME_LENGTH) {
536       if (ua) {
537          bsendmsg(ua, _("Volume name too long.\n"));
538       }
539       return 0;
540    }
541    if (len == 0) {
542       if (ua) {
543          bsendmsg(ua, _("Volume name must be at least one character long.\n"));
544       }
545       return 0;
546    }
547    return 1;
548 }
549
550 static int send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 
551                               POOL_DBR *pr, int relabel, bool media_record_exists)
552 {
553    BSOCK *sd;
554    char dev_name[MAX_NAME_LENGTH];
555    int ok = FALSE;
556
557    if (!(sd=open_sd_bsock(ua))) {
558       return 0;
559    }
560    bstrncpy(dev_name, ua->jcr->store->dev_name, sizeof(dev_name));
561    bash_spaces(dev_name);
562    bash_spaces(mr->VolumeName);
563    bash_spaces(mr->MediaType);
564    bash_spaces(pr->Name);
565    if (relabel) {
566       bash_spaces(omr->VolumeName);
567       bnet_fsend(sd, "relabel %s OldName=%s NewName=%s PoolName=%s MediaType=%s Slot=%d", 
568          dev_name, omr->VolumeName, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
569       bsendmsg(ua, _("Sending relabel command from \"%s\" to \"%s\" ...\n"),
570          omr->VolumeName, mr->VolumeName);
571    } else {
572       bnet_fsend(sd, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d", 
573          dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
574       bsendmsg(ua, _("Sending label command for Volume \"%s\" Slot %d ...\n"), 
575          mr->VolumeName, mr->Slot);
576       Dmsg5(200, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d\n", 
577          dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot);
578    }
579
580    while (bnet_recv(sd) >= 0) {
581       bsendmsg(ua, "%s", sd->msg);
582       if (strncmp(sd->msg, "3000 OK label.", 14) == 0) {
583          ok = TRUE;
584       } 
585    }
586    unbash_spaces(mr->VolumeName);
587    unbash_spaces(mr->MediaType);
588    unbash_spaces(pr->Name);
589    mr->LabelDate = time(NULL);
590    if (ok) {
591       if (media_record_exists) {      /* we update it */
592          mr->VolBytes = 1;
593          mr->InChanger = 1;
594          if (!db_update_media_record(ua->jcr, ua->db, mr)) {
595              bsendmsg(ua, "%s", db_strerror(ua->db));
596              ok = FALSE;
597          }
598       } else {                        /* create the media record */
599          set_pool_dbr_defaults_in_media_dbr(mr, pr);
600          mr->VolBytes = 1;               /* flag indicating Volume labeled */
601          mr->InChanger = 1;
602          if (db_create_media_record(ua->jcr, ua->db, mr)) {
603             bsendmsg(ua, _("Catalog record for Volume \"%s\", Slot %d  successfully created.\n"),
604             mr->VolumeName, mr->Slot);
605          } else {
606             bsendmsg(ua, "%s", db_strerror(ua->db));
607             ok = FALSE;
608          }
609       }
610    } else {
611       bsendmsg(ua, _("Label command failed.\n"));
612    }
613    return ok;
614 }
615
616 static BSOCK *open_sd_bsock(UAContext *ua) 
617 {
618    STORE *store = ua->jcr->store;
619
620    if (!ua->jcr->store_bsock) {
621       bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"), 
622          store->hdr.name, store->address, store->SDport);
623       if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
624          bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
625          return NULL;
626       }
627    }
628    return ua->jcr->store_bsock;
629 }
630
631 static void close_sd_bsock(UAContext *ua)
632 {
633    if (ua->jcr->store_bsock) {
634       bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
635       bnet_close(ua->jcr->store_bsock);
636       ua->jcr->store_bsock = NULL;
637    }
638 }
639
640 static char *get_volume_name_from_SD(UAContext *ua, int Slot) 
641 {
642    STORE *store = ua->jcr->store;
643    BSOCK *sd;
644    char dev_name[MAX_NAME_LENGTH];
645    char *VolName = NULL;
646    int rtn_slot;
647
648    if (!(sd=open_sd_bsock(ua))) {
649       return NULL;
650    }
651    bstrncpy(dev_name, store->dev_name, sizeof(dev_name));
652    bash_spaces(dev_name);
653    /* Ask for autochanger list of volumes */
654    bnet_fsend(sd, _("readlabel %s Slot=%d\n"), dev_name, Slot);
655    Dmsg1(100, "Sent: %s", sd->msg);
656
657    /* Get Volume name in this Slot */
658    while (bnet_recv(sd) >= 0) {
659       bsendmsg(ua, "%s", sd->msg);
660       if (strncmp(sd->msg, "3001 Volume=", 12) == 0) {
661          VolName = (char *)malloc(sd->msglen);
662          if (sscanf(sd->msg, "3001 Volume=%s Slot=%d", VolName, &rtn_slot) == 2) {
663             break;
664          }
665          free(VolName);
666          VolName = NULL;
667       }
668    }
669    Dmsg1(200, "get_vol_name=%s\n", NPRT(VolName));
670    return VolName;
671 }
672
673 /*
674  * We get the slot list from the Storage daemon.
675  *  If scan is set, we return all slots found,
676  *  otherwise, we return only slots with valid barcodes (Volume names)
677  */
678 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan) 
679 {
680    STORE *store = ua->jcr->store;
681    char dev_name[MAX_NAME_LENGTH];
682    BSOCK *sd;
683    vol_list_t *vl;
684    vol_list_t *vol_list = NULL;
685
686
687    if (!(sd=open_sd_bsock(ua))) {
688       return NULL;
689    }
690
691    bstrncpy(dev_name, store->dev_name, sizeof(dev_name));
692    bash_spaces(dev_name);
693    /* Ask for autochanger list of volumes */
694    bnet_fsend(sd, _("autochanger list %s \n"), dev_name);
695
696    /* Read and organize list of Volumes */
697    while (bnet_recv(sd) >= 0) {
698       char *p;
699       int Slot;
700       strip_trailing_junk(sd->msg);
701
702       /* Check for returned SD messages */
703       if (sd->msg[0] == '3'     && B_ISDIGIT(sd->msg[1]) &&
704           B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
705           sd->msg[4] == ' ') {
706          bsendmsg(ua, "%s\n", sd->msg);   /* pass them on to user */
707          continue;
708       }
709
710       /* Validate Slot: if scanning, otherwise  Slot:Barcode */
711       p = strchr(sd->msg, ':');
712       if (scan && p) {
713          /* Scanning -- require only valid slot */
714          Slot = atoi(sd->msg);
715          if (Slot <= 0) {
716             p--;
717             *p = ':';
718             bsendmsg(ua, _("Invalid Slot number: %s\n"), sd->msg); 
719             continue;
720          }
721       } else {
722          /* Not scanning */
723          if (p && strlen(p) > 1) {
724             *p++ = 0;
725             if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
726                p--;
727                *p = ':';
728                bsendmsg(ua, _("Invalid Slot number: %s\n"), sd->msg); 
729                continue;
730             }
731          } else {
732             continue;
733          }
734          if (!is_volume_name_legal(ua, p)) {
735             p--;
736             *p = ':';
737             bsendmsg(ua, _("Invalid Volume name: %s\n"), sd->msg); 
738             continue;
739          }
740       }
741
742       /* Add Slot and VolumeName to list */
743       vl = (vol_list_t *)malloc(sizeof(vol_list_t));
744       vl->Slot = Slot;
745       if (p) {
746          if (*p == ':') {
747             p++;                      /* skip separator */
748          }
749          vl->VolName = bstrdup(p);
750       } else {
751          vl->VolName = NULL;
752       }
753       Dmsg2(100, "Add slot=%d Vol=%s to list.\n", vl->Slot, NPRT(vl->VolName));
754       if (!vol_list) {
755          vl->next = vol_list;
756          vol_list = vl;
757       } else {
758          /* Add new entry to end of list */
759          for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
760             if (!tvl->next) {
761                tvl->next = vl;
762                vl->next = NULL;
763                break;
764             }
765          }
766       }
767    }
768    close_sd_bsock(ua);
769    return vol_list;
770 }
771
772 static void free_vol_list(vol_list_t *vol_list)
773 {
774    vol_list_t *vl;
775    /* Free list */
776    for (vl=vol_list; vl; ) {
777       vol_list_t *ovl;
778       if (vl->VolName) {
779          free(vl->VolName);
780       }
781       ovl = vl;
782       vl = vl->next;
783       free(ovl);
784    }
785 }
786
787
788 /*
789  * Check if this is a cleaning tape by comparing the Volume name
790  *  with the Cleaning Prefix. If they match, this is a cleaning 
791  *  tape.
792  */
793 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
794 {
795    /* Find Pool resource */
796    ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
797    if (!ua->jcr->pool) {
798       bsendmsg(ua, _("Pool \"%s\" resource not found!\n"), pr->Name);
799       return true;
800    }
801    if (ua->jcr->pool->cleaning_prefix == NULL) {
802       return false;
803    }
804    Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
805       ua->jcr->pool->cleaning_prefix, mr->VolumeName,
806       strlen(ua->jcr->pool->cleaning_prefix), 
807       strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
808                   strlen(ua->jcr->pool->cleaning_prefix)));
809    return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
810                   strlen(ua->jcr->pool->cleaning_prefix)) == 0;
811 }