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