]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_label.c
Tweak debug message in ua_label
[bacula/bacula] / bacula / src / dird / ua_label.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2003-2012 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- Tape labeling commands
31  *
32  *     Kern Sibbald, April MMIII
33  *
34  */
35
36 #include "bacula.h"
37 #include "dird.h"
38
39 /* Slot list definition */
40 typedef struct s_vol_list {
41    struct s_vol_list *next;
42    char *VolName;
43    int Slot;
44 } vol_list_t;
45
46
47 /* Forward referenced functions */
48 static int do_label(UAContext *ua, const char *cmd, int relabel);
49 static void label_from_barcodes(UAContext *ua, int drive);
50 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
51                POOL_DBR *pr, int relabel, bool media_record_exits, int drive);
52 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan);
53 static void free_vol_list(vol_list_t *vol_list);
54 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr);
55 static BSOCK *open_sd_bsock(UAContext *ua);
56 static void close_sd_bsock(UAContext *ua);
57 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive);
58 static int get_num_slots_from_SD(UAContext *ua);
59
60
61 /*
62  * Label a tape
63  *
64  *   label storage=xxx volume=vvv
65  */
66 int label_cmd(UAContext *ua, const char *cmd)
67 {
68    return do_label(ua, cmd, 0);       /* standard label */
69 }
70
71 int relabel_cmd(UAContext *ua, const char *cmd)
72 {
73    return do_label(ua, cmd, 1);      /* relabel tape */
74 }
75
76 static bool get_user_slot_list(UAContext *ua, char *slot_list, int num_slots)
77 {
78    int i;
79    const char *msg;
80
81    /* slots are numbered 1 to num_slots */
82    for (int i=0; i <= num_slots; i++) {
83       slot_list[i] = 0;
84    }
85    i = find_arg_with_value(ua, "slots");
86    if (i == -1) {  /* not found */
87       i = find_arg_with_value(ua, "slot");
88    }
89    if (i > 0) {
90       /* scan slot list in ua->argv[i] */
91       char *p, *e, *h;
92       int beg, end;
93
94       strip_trailing_junk(ua->argv[i]);
95       for (p=ua->argv[i]; p && *p; p=e) {
96          /* Check for list */
97          e = strchr(p, ',');
98          if (e) {
99             *e++ = 0;
100          }
101          /* Check for range */
102          h = strchr(p, '-');             /* range? */
103          if (h == p) {
104             msg = _("Negative numbers not permitted\n");
105             goto bail_out;
106          }
107          if (h) {
108             *h++ = 0;
109             if (!is_an_integer(h)) {
110                msg = _("Range end is not integer.\n");
111                goto bail_out;
112             }
113             skip_spaces(&p);
114             if (!is_an_integer(p)) {
115                msg = _("Range start is not an integer.\n");
116                goto bail_out;
117             }
118             beg = atoi(p);
119             end = atoi(h);
120             if (end < beg) {
121                msg = _("Range end not bigger than start.\n");
122                goto bail_out;
123             }
124          } else {
125             skip_spaces(&p);
126             if (!is_an_integer(p)) {
127                msg = _("Input value is not an integer.\n");
128                goto bail_out;
129             }
130             beg = end = atoi(p);
131          }
132          if (beg <= 0 || end <= 0) {
133             msg = _("Values must be be greater than zero.\n");
134             goto bail_out;
135          }
136          if (end > num_slots) {
137             msg = _("Slot too large.\n");
138             goto bail_out;
139          }
140          for (i=beg; i<=end; i++) {
141             slot_list[i] = 1;         /* Turn on specified range */
142          }
143       }
144    } else {
145       /* Turn everything on */
146       for (i=1; i <= num_slots; i++) {
147          slot_list[i] = 1;
148       }
149    }
150    if (debug_level >= 100) {
151       Dmsg0(100, "Slots turned on:\n");
152       for (i=1; i <= num_slots; i++) {
153          if (slot_list[i]) {
154             Dmsg1(100, "%d\n", i);
155          }
156       }
157    }
158    return true;
159
160 bail_out:
161    Dmsg1(100, "Problem with user selection ERR=%s\n", msg);
162    return false;
163 }
164
165 /*
166  * Update Slots corresponding to Volumes in autochanger
167  */
168 void update_slots(UAContext *ua)
169 {
170    USTORE store;
171    vol_list_t *vl, *vol_list = NULL;
172    MEDIA_DBR mr;
173    char *slot_list;
174    bool scan;
175    int max_slots;
176    int drive;
177    int Enabled = 1;
178    bool have_enabled;
179    int i;
180
181
182    if (!open_client_db(ua)) {
183       return;
184    }
185    store.store = get_storage_resource(ua, true/*arg is storage*/);
186    if (!store.store) {
187       return;
188    }
189    pm_strcpy(store.store_source, _("command line"));
190    set_wstorage(ua->jcr, &store);
191    drive = get_storage_drive(ua, store.store);
192
193    scan = find_arg(ua, NT_("scan")) >= 0;
194    if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
195       Enabled = get_enabled(ua, ua->argv[i]);
196       if (Enabled < 0) {
197          return;
198       }
199       have_enabled = true;
200    } else {
201       have_enabled = false;
202    }
203
204    max_slots = get_num_slots_from_SD(ua);
205    Dmsg1(100, "max_slots=%d\n", max_slots);
206    if (max_slots <= 0) {
207       ua->warning_msg(_("No slots in changer to scan.\n"));
208       return;
209    }
210    slot_list = (char *)malloc(max_slots+1);
211    if (!get_user_slot_list(ua, slot_list, max_slots)) {
212       free(slot_list);
213       return;
214    }
215
216    vol_list = get_vol_list_from_SD(ua, scan);
217
218    if (!vol_list) {
219       ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
220       goto bail_out;
221    }
222
223    /* First zap out any InChanger with StorageId=0 */
224    db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL);
225
226    /* Walk through the list updating the media records */
227    for (vl=vol_list; vl; vl=vl->next) {
228       if (vl->Slot > max_slots) {
229          ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
230             vl->Slot, max_slots);
231          continue;
232       }
233       /* Check if user wants us to look at this slot */
234       if (!slot_list[vl->Slot]) {
235          Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
236          continue;
237       }
238       /* If scanning, we read the label rather than the barcode */
239       if (scan) {
240          if (vl->VolName) {
241             free(vl->VolName);
242             vl->VolName = NULL;
243          }
244          vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive);
245          Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot);
246       }
247       slot_list[vl->Slot] = 0;        /* clear Slot */
248       mr.Slot = vl->Slot;
249       mr.InChanger = 1;
250       mr.MediaId = 0;                 /* Force using VolumeName */
251       if (vl->VolName) {
252          bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
253       } else {
254          mr.VolumeName[0] = 0;
255       }
256       set_storageid_in_mr(store.store, &mr);
257       Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
258             mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
259       db_lock(ua->db);
260       /* Set InChanger to zero for this Slot */
261       db_make_inchanger_unique(ua->jcr, ua->db, &mr);
262       db_unlock(ua->db);
263       Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
264             mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
265       if (!vl->VolName) {
266          Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot);
267          ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot);
268          continue;
269       }
270       db_lock(ua->db);
271       Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
272             mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
273       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
274          Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
275             mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
276          /* If Slot, Inchanger, and StorageId have changed, update the Media record */
277          if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
278             mr.Slot = vl->Slot;
279             mr.InChanger = 1;
280             if (have_enabled) {
281                mr.Enabled = Enabled;
282             }
283             set_storageid_in_mr(store.store, &mr);
284             if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
285                ua->error_msg("%s", db_strerror(ua->db));
286             } else {
287                ua->info_msg(_(
288                  "Catalog record for Volume \"%s\" updated to reference slot %d.\n"),
289                  mr.VolumeName, mr.Slot);
290             }
291          } else {
292             ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"),
293                mr.VolumeName);
294          }
295          db_unlock(ua->db);
296          continue;
297       } else {
298          ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"),
299              mr.VolumeName, vl->Slot);
300       }
301       db_unlock(ua->db);
302    }
303    mr.clear();
304    mr.InChanger = 1;
305    set_storageid_in_mr(store.store, &mr);
306    db_lock(ua->db);
307    for (int i=1; i <= max_slots; i++) {
308       if (slot_list[i]) {
309          mr.Slot = i;
310          /* Set InChanger to zero for this Slot */
311          db_make_inchanger_unique(ua->jcr, ua->db, &mr);
312       }
313    }
314    db_unlock(ua->db);
315
316 bail_out:
317
318    free_vol_list(vol_list);
319    free(slot_list);
320    close_sd_bsock(ua);
321
322    return;
323 }
324
325
326 /*
327  * Common routine for both label and relabel
328  */
329 static int do_label(UAContext *ua, const char *cmd, int relabel)
330 {
331    USTORE store;
332    BSOCK *sd;
333    char dev_name[MAX_NAME_LENGTH];
334    MEDIA_DBR mr, omr;
335    POOL_DBR pr;
336    bool print_reminder = true;
337    bool label_barcodes = false;
338    int ok = FALSE;
339    int i, j;
340    int drive;
341    bool media_record_exists = false;
342    static const char *barcode_keyword[] = {
343       "barcode",
344       "barcodes",
345       NULL};
346
347
348    memset(&pr, 0, sizeof(pr));
349    if (!open_client_db(ua)) {
350       return 1;
351    }
352
353    /* Look for one of the barcode keywords */
354    if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) {
355       /* Now find the keyword in the list */
356       if ((j = find_arg(ua, barcode_keyword[i])) > 0) {
357          *ua->argk[j] = 0;      /* zap barcode keyword */
358       }
359       label_barcodes = true;
360    }
361
362    store.store = get_storage_resource(ua, true/*use default*/);
363    if (!store.store) {
364       return 1;
365    }
366    pm_strcpy(store.store_source, _("command line"));
367    set_wstorage(ua->jcr, &store);
368    drive = get_storage_drive(ua, store.store);
369
370    if (label_barcodes) {
371       label_from_barcodes(ua, drive);
372       return 1;
373    }
374
375    /* If relabel get name of Volume to relabel */
376    if (relabel) {
377       /* Check for oldvolume=name */
378       i = find_arg_with_value(ua, "oldvolume");
379       if (i >= 0) {
380          bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
381          if (db_get_media_record(ua->jcr, ua->db, &omr)) {
382             goto checkVol;
383          }
384          ua->error_msg("%s", db_strerror(ua->db));
385       }
386       /* No keyword or Vol not found, ask user to select */
387       if (!select_media_dbr(ua, &omr)) {
388          return 1;
389       }
390
391       /* Require Volume to be Purged or Recycled */
392 checkVol:
393       if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) {
394          ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"),
395             omr.VolumeName, omr.VolStatus);
396          return 1;
397       }
398    }
399
400    /* Check for volume=NewVolume */
401    i = find_arg_with_value(ua, "volume");
402    if (i >= 0) {
403       pm_strcpy(ua->cmd, ua->argv[i]);
404       goto checkName;
405    }
406
407    /* Get a new Volume name */
408    for ( ;; ) {
409       media_record_exists = false;
410       if (!get_cmd(ua, _("Enter new Volume name: "))) {
411          return 1;
412       }
413 checkName:
414       if (!is_volume_name_legal(ua, ua->cmd)) {
415          continue;
416       }
417
418       bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName));
419       /* If VolBytes are zero the Volume is not labeled */
420       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
421          if (mr.VolBytes != 0) {
422              ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"),
423                 mr.VolumeName);
424              continue;
425           }
426           media_record_exists = true;
427       }
428       break;                          /* Got it */
429    }
430
431    /* If autochanger, request slot */
432    i = find_arg_with_value(ua, "slot");
433    if (i >= 0) {
434       mr.Slot = atoi(ua->argv[i]);
435       if (mr.Slot < 0) {
436          mr.Slot = 0;
437       }
438       mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
439    } else if (store.store->autochanger) {
440       if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
441          return 1;
442       }
443       mr.Slot = ua->pint32_val;
444       if (mr.Slot < 0) {
445          mr.Slot = 0;
446       }
447       mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
448    }
449    set_storageid_in_mr(store.store, &mr);
450
451    bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
452
453    /* Must select Pool if not already done */
454    if (pr.PoolId == 0) {
455       memset(&pr, 0, sizeof(pr));
456       if (!select_pool_dbr(ua, &pr)) {
457          return 1;
458       }
459    }
460
461    ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive);
462
463    if (ok) {
464       sd = ua->jcr->store_bsock;
465       if (relabel) {
466          /* Delete the old media record */
467          if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
468             ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"),
469                omr.VolumeName, db_strerror(ua->db));
470          } else {
471             ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"),
472                omr.VolumeName);
473             /* Update the number of Volumes in the pool */
474             pr.NumVols--;
475             if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
476                ua->error_msg("%s", db_strerror(ua->db));
477             }
478          }
479       }
480       if (ua->automount) {
481          bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
482          ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
483          bash_spaces(dev_name);
484          bnet_fsend(sd, "mount %s drive=%d", dev_name, drive);
485          unbash_spaces(dev_name);
486          while (bnet_recv(sd) >= 0) {
487             ua->send_msg("%s", sd->msg);
488             /* Here we can get
489              *  3001 OK mount. Device=xxx      or
490              *  3001 Mounted Volume vvvv
491              *  3002 Device "DVD-Writer" (/dev/hdc) is mounted.
492              *  3906 is cannot mount non-tape
493              * So for those, no need to print a reminder
494              */
495             if (strncmp(sd->msg, "3001 ", 5) == 0 ||
496                 strncmp(sd->msg, "3002 ", 5) == 0 ||
497                 strncmp(sd->msg, "3906 ", 5) == 0) {
498                print_reminder = false;
499             }
500          }
501       }
502    }
503    if (print_reminder) {
504       ua->info_msg(_("Do not forget to mount the drive!!!\n"));
505    }
506    close_sd_bsock(ua);
507
508    return 1;
509 }
510
511 /*
512  * Request SD to send us the slot:barcodes, then wiffle
513  *  through them all labeling them.
514  */
515 static void label_from_barcodes(UAContext *ua, int drive)
516 {
517    STORE *store = ua->jcr->wstore;
518    POOL_DBR pr;
519    MEDIA_DBR mr, omr;
520    vol_list_t *vl, *vol_list = NULL;
521    bool media_record_exists;
522    char *slot_list;
523    int max_slots;
524
525   
526    max_slots = get_num_slots_from_SD(ua);
527    if (max_slots <= 0) {
528       ua->warning_msg(_("No slots in changer to scan.\n"));
529       return;
530    }
531    slot_list = (char *)malloc(max_slots+1);
532    if (!get_user_slot_list(ua, slot_list, max_slots)) {
533       goto bail_out;
534    }
535
536    vol_list = get_vol_list_from_SD(ua, false /*no scan*/);
537
538    if (!vol_list) {
539       ua->warning_msg(_("No Volumes found to label, or no barcodes.\n"));
540       goto bail_out;
541    }
542
543    /* Display list of Volumes and ask if he really wants to proceed */
544    ua->send_msg(_("The following Volumes will be labeled:\n"
545                   "Slot  Volume\n"
546                   "==============\n"));
547    for (vl=vol_list; vl; vl=vl->next) {
548       if (!vl->VolName || !slot_list[vl->Slot]) {
549          continue;
550       }
551       ua->send_msg("%4d  %s\n", vl->Slot, vl->VolName);
552    }
553    if (!get_yesno(ua, _("Do you want to label these Volumes? (yes|no): ")) ||
554        (ua->pint32_val == 0)) {
555       goto bail_out;
556    }
557    /* Select a pool */
558    memset(&pr, 0, sizeof(pr));
559    if (!select_pool_dbr(ua, &pr)) {
560       goto bail_out;
561    }
562
563    /* Fire off the label requests */
564    for (vl=vol_list; vl; vl=vl->next) {
565       if (!vl->VolName || !slot_list[vl->Slot]) {
566          continue;
567       }
568       mr.clear();
569       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
570       media_record_exists = false;
571       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
572           if (mr.VolBytes != 0) {
573              ua->warning_msg(_("Media record for Slot %d Volume \"%s\" already exists.\n"),
574                 vl->Slot, mr.VolumeName);
575              mr.Slot = vl->Slot;
576              mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
577              set_storageid_in_mr(store, &mr);
578              if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
579                 ua->error_msg(_("Error setting InChanger: ERR=%s"), db_strerror(ua->db));
580              }
581              continue;
582           }
583           media_record_exists = true;
584       }
585       mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
586       set_storageid_in_mr(store, &mr);
587       /*
588        * Deal with creating cleaning tape here. Normal tapes created in
589        *  send_label_request() below
590        */
591       if (is_cleaning_tape(ua, &mr, &pr)) {
592          if (media_record_exists) {      /* we update it */
593             mr.VolBytes = 1;             /* any bytes to indicate it exists */
594             bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
595             mr.MediaType[0] = 0;
596             set_storageid_in_mr(store, &mr);
597             if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
598                 ua->error_msg("%s", db_strerror(ua->db));
599             }
600          } else {                        /* create the media record */
601             if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
602                ua->error_msg(_("Maximum pool Volumes=%d reached.\n"), pr.MaxVols);
603                goto bail_out;
604             }
605             set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
606             bstrncpy(mr.VolStatus, "Cleaning", sizeof(mr.VolStatus));
607             mr.MediaType[0] = 0;
608             set_storageid_in_mr(store, &mr);
609             if (db_create_media_record(ua->jcr, ua->db, &mr)) {
610                ua->send_msg(_("Catalog record for cleaning tape \"%s\" successfully created.\n"),
611                   mr.VolumeName);
612                pr.NumVols++;          /* this is a bit suspect */
613                if (!db_update_pool_record(ua->jcr, ua->db, &pr)) {
614                   ua->error_msg("%s", db_strerror(ua->db));
615                }
616             } else {
617                ua->error_msg(_("Catalog error on cleaning tape: %s"), db_strerror(ua->db));
618             }
619          }
620          continue;                    /* done, go handle next volume */
621       }
622       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
623
624       mr.Slot = vl->Slot;
625       send_label_request(ua, &mr, &omr, &pr, 0, media_record_exists, drive);
626    }
627
628
629 bail_out:
630    free(slot_list);
631    free_vol_list(vol_list);
632    close_sd_bsock(ua);
633
634    return;
635 }
636
637 /*
638  * Check if the Volume name has legal characters
639  * If ua is non-NULL send the message
640  */
641 bool is_volume_name_legal(UAContext *ua, const char *name)
642 {
643    int len;
644    const char *p;
645    const char *accept = ":.-_";
646
647    /* Restrict the characters permitted in the Volume name */
648    for (p=name; *p; p++) {
649       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
650          continue;
651       }
652       if (ua) {
653          ua->error_msg(_("Illegal character \"%c\" in a volume name.\n"), *p);
654       }
655       return 0;
656    }
657    len = strlen(name);
658    if (len >= MAX_NAME_LENGTH) {
659       if (ua) {
660          ua->error_msg(_("Volume name too long.\n"));
661       }
662       return 0;
663    }
664    if (len == 0) {
665       if (ua) {
666          ua->error_msg(_("Volume name must be at least one character long.\n"));
667       }
668       return 0;
669    }
670    return 1;
671 }
672
673 /*
674  * NOTE! This routine opens the SD socket but leaves it open
675  */
676 static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
677                                POOL_DBR *pr, int relabel, bool media_record_exists,
678                                int drive)
679 {
680    BSOCK *sd;
681    char dev_name[MAX_NAME_LENGTH];
682    bool ok = false;
683    bool is_dvd = false;
684    uint64_t VolBytes = 0;
685
686    if (!(sd=open_sd_bsock(ua))) {
687       return false;
688    }
689    bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
690    bash_spaces(dev_name);
691    bash_spaces(mr->VolumeName);
692    bash_spaces(mr->MediaType);
693    bash_spaces(pr->Name);
694    if (relabel) {
695       bash_spaces(omr->VolumeName);
696       sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s "
697                      "MediaType=%s Slot=%d drive=%d",
698                  dev_name, omr->VolumeName, mr->VolumeName, pr->Name, 
699                  mr->MediaType, mr->Slot, drive);
700       ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"),
701          omr->VolumeName, mr->VolumeName);
702    } else {
703       sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s "
704                      "Slot=%d drive=%d",
705                  dev_name, mr->VolumeName, pr->Name, mr->MediaType, 
706                  mr->Slot, drive);
707       ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"),
708          mr->VolumeName, mr->Slot);
709       Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n",
710          dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive);
711    }
712
713    while (sd->recv() >= 0) {
714       int dvd;
715       ua->send_msg("%s", sd->msg);
716       if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes,
717                  &dvd) == 2) {
718          is_dvd = dvd;
719          ok = true;
720       }
721    }
722    unbash_spaces(mr->VolumeName);
723    unbash_spaces(mr->MediaType);
724    unbash_spaces(pr->Name);
725    mr->LabelDate = time(NULL);
726    mr->set_label_date = true;
727    if (is_dvd) {
728       /* We know that a freshly labelled DVD has 1 VolParts */
729       /* This does not apply to auto-labelled DVDs. */
730       mr->VolParts = 1;
731    }
732    if (ok) {
733       if (media_record_exists) {      /* we update it */
734          mr->VolBytes = VolBytes;
735          mr->InChanger = mr->Slot > 0;  /* if slot give assume in changer */
736          set_storageid_in_mr(ua->jcr->wstore, mr);
737          if (!db_update_media_record(ua->jcr, ua->db, mr)) {
738              ua->error_msg("%s", db_strerror(ua->db));
739              ok = false;
740          }
741       } else {                        /* create the media record */
742          set_pool_dbr_defaults_in_media_dbr(mr, pr);
743          mr->VolBytes = VolBytes;
744          mr->InChanger = mr->Slot > 0;  /* if slot give assume in changer */
745          mr->Enabled = 1;
746          set_storageid_in_mr(ua->jcr->wstore, mr);
747          if (db_create_media_record(ua->jcr, ua->db, mr)) {
748             ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d  successfully created.\n"),
749             mr->VolumeName, mr->Slot);
750             /* Update number of volumes in pool */
751             pr->NumVols++;
752             if (!db_update_pool_record(ua->jcr, ua->db, pr)) {
753                ua->error_msg("%s", db_strerror(ua->db));
754             }
755          } else {
756             ua->error_msg("%s", db_strerror(ua->db));
757             ok = false;
758          }
759       }
760    } else {
761       ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName);
762    }
763    return ok;
764 }
765
766 static BSOCK *open_sd_bsock(UAContext *ua)
767 {
768    STORE *store = ua->jcr->wstore;
769
770    if (!ua->jcr->store_bsock) {
771       ua->send_msg(_("Connecting to Storage daemon %s at %s:%d ...\n"),
772          store->name(), store->address, store->SDport);
773       if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
774          ua->error_msg(_("Failed to connect to Storage daemon.\n"));
775          return NULL;
776       }
777    }
778    return ua->jcr->store_bsock;
779 }
780
781 static void close_sd_bsock(UAContext *ua)
782 {
783    if (ua->jcr->store_bsock) {
784       bnet_sig(ua->jcr->store_bsock, BNET_TERMINATE);
785       bnet_close(ua->jcr->store_bsock);
786       ua->jcr->store_bsock = NULL;
787    }
788 }
789
790 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
791 {
792    STORE *store = ua->jcr->wstore;
793    BSOCK *sd;
794    char dev_name[MAX_NAME_LENGTH];
795    char *VolName = NULL;
796    int rtn_slot;
797
798    if (!(sd=open_sd_bsock(ua))) {
799       ua->error_msg(_("Could not open SD socket.\n"));
800       return NULL;
801    }
802    bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
803    bash_spaces(dev_name);
804    /* Ask for autochanger list of volumes */
805    sd->fsend(NT_("readlabel %s Slot=%d drive=%d\n"), dev_name, Slot, drive);
806    Dmsg1(100, "Sent: %s", sd->msg);
807
808    /* Get Volume name in this Slot */
809    while (sd->recv() >= 0) {
810       ua->send_msg("%s", sd->msg);
811       Dmsg1(100, "Got: %s", sd->msg);
812       if (strncmp(sd->msg, NT_("3001 Volume="), 12) == 0) {
813          VolName = (char *)malloc(sd->msglen);
814          if (sscanf(sd->msg, NT_("3001 Volume=%s Slot=%d"), VolName, &rtn_slot) == 2) {
815             break;
816          }
817          free(VolName);
818          VolName = NULL;
819       }
820    }
821    close_sd_bsock(ua);
822    Dmsg1(100, "get_vol_name=%s\n", NPRT(VolName));
823    return VolName;
824 }
825
826 /*
827  * We get the slot list from the Storage daemon.
828  *  If scan is set, we return all slots found,
829  *  otherwise, we return only slots with valid barcodes (Volume names)
830  */
831 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
832 {
833    STORE *store = ua->jcr->wstore;
834    char dev_name[MAX_NAME_LENGTH];
835    BSOCK *sd;
836    vol_list_t *vl;
837    vol_list_t *vol_list = NULL;
838
839
840    if (!(sd=open_sd_bsock(ua))) {
841       return NULL;
842    }
843
844    bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
845    bash_spaces(dev_name);
846    /* Ask for autochanger list of volumes */
847    bnet_fsend(sd, NT_("autochanger list %s \n"), dev_name);
848
849    /* Read and organize list of Volumes */
850    while (bnet_recv(sd) >= 0) {
851       char *p;
852       int Slot;
853       strip_trailing_junk(sd->msg);
854
855       /* Check for returned SD messages */
856       if (sd->msg[0] == '3'     && B_ISDIGIT(sd->msg[1]) &&
857           B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
858           sd->msg[4] == ' ') {
859          ua->send_msg("%s\n", sd->msg);   /* pass them on to user */
860          continue;
861       }
862
863       /* Validate Slot: if scanning, otherwise  Slot:Barcode */
864       p = strchr(sd->msg, ':');
865       if (scan && p) {
866          /* Scanning -- require only valid slot */
867          Slot = atoi(sd->msg);
868          if (Slot <= 0) {
869             p--;
870             *p = ':';
871             ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
872             continue;
873          }
874       } else {
875          /* Not scanning */
876          if (p && strlen(p) > 1) {
877             *p++ = 0;
878             if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) {
879                p--;
880                *p = ':';
881                ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg);
882                continue;
883             }
884          } else {
885             continue;
886          }
887          if (!is_volume_name_legal(ua, p)) {
888             p--;
889             *p = ':';
890             ua->error_msg(_("Invalid Volume name: %s\n"), sd->msg);
891             continue;
892          }
893       }
894
895       /* Add Slot and VolumeName to list */
896       vl = (vol_list_t *)malloc(sizeof(vol_list_t));
897       vl->Slot = Slot;
898       if (p) {
899          if (*p == ':') {
900             p++;                      /* skip separator */
901          }
902          vl->VolName = bstrdup(p);
903       } else {
904          vl->VolName = NULL;
905       }
906       Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName));
907       if (!vol_list) {
908          vl->next = vol_list;
909          vol_list = vl;
910       } else {
911          vol_list_t *prev=vol_list;
912          /* Add new entry to the right place in the list */
913          for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) {
914             if (tvl->Slot > vl->Slot) {
915                /* no previous item, update vol_list directly */
916                if (prev == vol_list) {  
917                   vl->next = vol_list;
918                   vol_list = vl;
919
920                } else {     /* replace the previous pointer */
921                   prev->next = vl;
922                   vl->next = tvl;
923                }
924                break;
925             }
926             /* we are at the end */
927             if (!tvl->next) {
928                tvl->next = vl;
929                vl->next = NULL;
930                break;
931             }
932             prev = tvl;
933          }
934       }
935    }
936    close_sd_bsock(ua);
937    return vol_list;
938 }
939
940 static void free_vol_list(vol_list_t *vol_list)
941 {
942    vol_list_t *vl;
943
944    /* Free list */
945    for (vl=vol_list; vl; ) {
946       vol_list_t *ovl;
947       if (vl->VolName) {
948          free(vl->VolName);
949       }
950       ovl = vl;
951       vl = vl->next;
952       free(ovl);
953    }
954 }
955
956 /*
957  * We get the number of slots in the changer from the SD
958  */
959 static int get_num_slots_from_SD(UAContext *ua)
960 {
961    STORE *store = ua->jcr->wstore;
962    char dev_name[MAX_NAME_LENGTH];
963    BSOCK *sd;
964    int slots = 0;
965
966
967    if (!(sd=open_sd_bsock(ua))) {
968       return 0;
969    }
970
971    bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
972    bash_spaces(dev_name);
973    /* Ask for autochanger number of slots */
974    sd->fsend(NT_("autochanger slots %s\n"), dev_name);
975
976    while (sd->recv() >= 0) {
977       if (sscanf(sd->msg, "slots=%d\n", &slots) == 1) {
978          break;
979       } else {
980          ua->send_msg("%s", sd->msg);
981       }
982    }
983    close_sd_bsock(ua);
984    ua->send_msg(_("Device \"%s\" has %d slots.\n"), store->dev_name(), slots);
985    return slots;
986 }
987
988 /*
989  * We get the number of drives in the changer from the SD
990  */
991 int get_num_drives_from_SD(UAContext *ua)
992 {
993    STORE *store = ua->jcr->wstore;
994    char dev_name[MAX_NAME_LENGTH];
995    BSOCK *sd;
996    int drives = 0;
997
998
999    if (!(sd=open_sd_bsock(ua))) {
1000       return 0;
1001    }
1002
1003    bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1004    bash_spaces(dev_name);
1005    /* Ask for autochanger number of slots */
1006    sd->fsend(NT_("autochanger drives %s\n"), dev_name);
1007
1008    while (sd->recv() >= 0) {
1009       if (sscanf(sd->msg, NT_("drives=%d\n"), &drives) == 1) {
1010          break;
1011       } else {
1012          ua->send_msg("%s", sd->msg);
1013       }
1014    }
1015    close_sd_bsock(ua);
1016 //   bsendmsg(ua, _("Device \"%s\" has %d drives.\n"), store->dev_name(), drives);
1017    return drives;
1018 }
1019
1020 /*
1021  * Check if this is a cleaning tape by comparing the Volume name
1022  *  with the Cleaning Prefix. If they match, this is a cleaning
1023  *  tape.
1024  */
1025 static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr)
1026 {
1027    /* Find Pool resource */
1028    ua->jcr->pool = (POOL *)GetResWithName(R_POOL, pr->Name);
1029    if (!ua->jcr->pool) {
1030       ua->error_msg(_("Pool \"%s\" resource not found for volume \"%s\"!\n"),
1031          pr->Name, mr->VolumeName);
1032       return false;
1033    }
1034    if (ua->jcr->pool->cleaning_prefix == NULL) {
1035       return false;
1036    }
1037    Dmsg4(100, "CLNprefix=%s: Vol=%s: len=%d strncmp=%d\n",
1038       ua->jcr->pool->cleaning_prefix, mr->VolumeName,
1039       strlen(ua->jcr->pool->cleaning_prefix),
1040       strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1041                   strlen(ua->jcr->pool->cleaning_prefix)));
1042    return strncmp(mr->VolumeName, ua->jcr->pool->cleaning_prefix,
1043                   strlen(ua->jcr->pool->cleaning_prefix)) == 0;
1044 }
1045
1046 static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name)
1047 {
1048    char ed1[50], ed2[50], ed3[50];
1049    POOL_DBR pr;
1050    MEDIA_DBR mr;
1051    /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */
1052    const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n";
1053    const char *slot_api_empty_format="%c|%i||||||||\n";
1054
1055    if (is_volume_name_legal(NULL, vol_name)) {
1056       bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName));
1057       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
1058          memset(&pr, 0, sizeof(POOL_DBR));
1059          pr.PoolId = mr.PoolId;
1060          if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1061             strcpy(pr.Name, "?");
1062          }
1063          ua->send_msg(slot_api_full_format, type,
1064                       Slot, mr.Slot, mr.VolumeName, 
1065                       edit_uint64(mr.VolBytes, ed1), 
1066                       mr.VolStatus, mr.MediaType, pr.Name, 
1067                       edit_uint64(mr.LastWritten, ed2),
1068                       edit_uint64(mr.LastWritten+mr.VolRetention, ed3));
1069          
1070       } else {                  /* Media unknown */
1071          ua->send_msg(slot_api_full_format,
1072                       type, Slot, 0, mr.VolumeName, "?", "?", "?", "?", 
1073                       "0", "0");
1074          
1075       }
1076    } else {
1077       ua->send_msg(slot_api_empty_format, type, Slot);
1078    }
1079 }         
1080
1081 /* 
1082  * Input (output of mxt-changer listall):
1083  *
1084  * Drive content:         D:Drive num:F:Slot loaded:Volume Name
1085  * D:0:F:2:vol2        or D:Drive num:E
1086  * D:1:F:42:vol42   
1087  * D:3:E
1088  *
1089  * Slot content:
1090  * S:1:F:vol1             S:Slot num:F:Volume Name
1091  * S:2:E               or S:Slot num:E
1092  * S:3:F:vol4
1093  *
1094  * Import/Export tray slots:
1095  * I:10:F:vol10           I:Slot num:F:Volume Name
1096  * I:11:E              or I:Slot num:E
1097  * I:12:F:vol40
1098  *
1099  * If a drive is loaded, the slot *should* be empty 
1100  * 
1101  * Output:
1102  *
1103  * Drive list:       D|Drive num|Slot loaded|Volume Name
1104  * D|0|45|vol45
1105  * D|1|42|vol42
1106  * D|3||
1107  *
1108  * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire
1109  *
1110  * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902
1111  * S|2||||||||
1112  * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907
1113  *
1114  * TODO: need to merge with status_slots()
1115  */
1116 void status_content(UAContext *ua, STORE *store)
1117 {
1118    int Slot, Drive;
1119    char type;
1120    char dev_name[MAX_NAME_LENGTH];
1121    char vol_name[MAX_NAME_LENGTH];
1122    BSOCK *sd;
1123    vol_list_t *vl=NULL, *vol_list = NULL;
1124
1125    if (!(sd=open_sd_bsock(ua))) {
1126       return;
1127    }
1128
1129    if (!open_client_db(ua)) {
1130       return;
1131    }
1132
1133    bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1134    bash_spaces(dev_name);
1135    /* Ask for autochanger list of volumes */
1136    bnet_fsend(sd, NT_("autochanger listall %s \n"), dev_name);
1137
1138    /* Read and organize list of Drive, Slots and I/O Slots */
1139    while (bnet_recv(sd) >= 0) {
1140       strip_trailing_junk(sd->msg);
1141
1142       /* Check for returned SD messages */
1143       if (sd->msg[0] == '3'     && B_ISDIGIT(sd->msg[1]) &&
1144           B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) &&
1145           sd->msg[4] == ' ') {
1146          ua->send_msg("%s\n", sd->msg);   /* pass them on to user */
1147          continue;
1148       }
1149
1150       Drive = Slot = -1;
1151       *vol_name = 0;
1152
1153       if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) {
1154          ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name);
1155
1156          /* we print information on the slot if we have a volume name */
1157          if (*vol_name) {
1158             /* Add Slot and VolumeName to list */
1159             vl = (vol_list_t *)malloc(sizeof(vol_list_t));
1160             vl->Slot = Slot;
1161             vl->VolName = bstrdup(vol_name);
1162             vl->next = vol_list;
1163             vol_list = vl;
1164          }
1165
1166       } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) {
1167          ua->send_msg("D|%d||\n", Drive);
1168
1169       } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) {
1170          content_send_info(ua, type, Slot, vol_name);
1171
1172       } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) {
1173          /* type can be S (slot) or I (Import/Export slot) */
1174          vol_list_t *prev=NULL;
1175          for (vl = vol_list; vl; vl = vl->next) {
1176             if (vl->Slot == Slot) {
1177                bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH);
1178
1179                /* remove the node */
1180                if (prev) {
1181                   prev->next = vl->next;
1182                } else {
1183                   vol_list = vl->next;
1184                }
1185                free(vl->VolName);
1186                free(vl);
1187                break;
1188             }
1189             prev = vl;
1190          }
1191          content_send_info(ua, type, Slot, vol_name);
1192
1193       } else {
1194          Dmsg1(10, "Discarding msg=%s\n", sd->msg);
1195       }
1196    }
1197    close_sd_bsock(ua);
1198 }
1199
1200 /*
1201  * Print slots from AutoChanger
1202  */
1203 void status_slots(UAContext *ua, STORE *store_r)
1204 {
1205    USTORE store;
1206    POOL_DBR pr;
1207    vol_list_t *vl, *vol_list = NULL;
1208    MEDIA_DBR mr;
1209    char *slot_list;
1210    int max_slots;
1211    int i=1;
1212    /* Slot | Volume | Status | MediaType | Pool */
1213    const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n";
1214
1215    if (ua->api) {
1216       status_content(ua, store_r);
1217       return;
1218    }
1219
1220    if (!open_client_db(ua)) {
1221       return;
1222    }
1223    store.store = store_r;
1224
1225    pm_strcpy(store.store_source, _("command line"));
1226    set_wstorage(ua->jcr, &store);
1227    get_storage_drive(ua, store.store);
1228
1229    max_slots = get_num_slots_from_SD(ua);
1230
1231    if (max_slots <= 0) {
1232       ua->warning_msg(_("No slots in changer to scan.\n"));
1233       return;
1234    }
1235    slot_list = (char *)malloc(max_slots+1);
1236    if (!get_user_slot_list(ua, slot_list, max_slots)) {
1237       free(slot_list);
1238       return;
1239    }
1240
1241    vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */);
1242
1243    if (!vol_list) {
1244       ua->warning_msg(_("No Volumes found, or no barcodes.\n"));
1245       goto bail_out;
1246    }
1247    ua->send_msg(_(" Slot |   Volume Name    |   Status  |     Media Type       |      Pool          |\n"));
1248    ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n"));
1249
1250    /* Walk through the list getting the media records */
1251    for (vl=vol_list; vl; vl=vl->next) {
1252       if (vl->Slot > max_slots) {
1253          ua->warning_msg(_("Slot %d greater than max %d ignored.\n"),
1254             vl->Slot, max_slots);
1255          continue;
1256       }
1257       /* Check if user wants us to look at this slot */
1258       if (!slot_list[vl->Slot]) {
1259          Dmsg1(100, "Skipping slot=%d\n", vl->Slot);
1260          continue;
1261       }
1262
1263       slot_list[vl->Slot] = 0;        /* clear Slot */
1264
1265       if (!vl->VolName) {
1266          Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot);
1267          ua->send_msg(slot_hformat,
1268                       vl->Slot, '*',
1269                       "?", "?", "?", "?");
1270          continue;
1271       }
1272
1273       /* Hope that slots are ordered */
1274       for (; i < vl->Slot; i++) {
1275          if (slot_list[i]) {
1276             ua->send_msg(slot_hformat,
1277                          i, ' ', "", "", "", "");
1278             slot_list[i]=0;
1279          }
1280       }
1281
1282       memset(&mr, 0, sizeof(MEDIA_DBR));
1283       bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName));
1284
1285       if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
1286          memset(&pr, 0, sizeof(POOL_DBR));
1287          pr.PoolId = mr.PoolId;
1288          if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
1289             strcpy(pr.Name, "?");
1290          }
1291
1292          /* Print information */
1293          ua->send_msg(slot_hformat,
1294                       vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'),
1295                       mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name);
1296
1297       } else {                  /* TODO: get information from catalog  */
1298          ua->send_msg(slot_hformat,
1299                       vl->Slot, '*',
1300                       mr.VolumeName, "?", "?", "?");
1301       }
1302    }
1303
1304    /* Display the rest of the autochanger
1305     */
1306    for (; i <= max_slots; i++) {
1307       if (slot_list[i]) {
1308          ua->send_msg(slot_hformat,
1309                       i, ' ', "", "", "", "");
1310          slot_list[i]=0;
1311       }
1312    }
1313
1314 bail_out:
1315
1316    free_vol_list(vol_list);
1317    free(slot_list);
1318    close_sd_bsock(ua);
1319
1320    return;
1321 }