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