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