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