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 * Routines for handling the autochanger.
22 *
23 * Written by Kern Sibbald, August MMII
24 *
25 */
26
27 #include "bacula.h" /* pull in global headers */
28 #include "stored.h" /* pull in Storage Deamon headers */
29
30 static const int dbglvl = 60;
31
32 /* Forward referenced functions */
33 static void lock_changer(DCR *dcr);
34 static void unlock_changer(DCR *dcr);
35 static bool unload_other_drive(DCR *dcr, int slot, bool writing);
36
is_virtual_autochanger()37 bool DCR::is_virtual_autochanger()
38 {
39 return device->changer_command &&
40 (device->changer_command[0] == 0 ||
41 strcmp(device->changer_command, "/dev/null") == 0);
42 }
43
44 /* Init all the autochanger resources found */
init_autochangers()45 bool init_autochangers()
46 {
47 bool OK = true;
48 AUTOCHANGER *changer;
49 /* Ensure that the media_type for each device is the same */
50 foreach_res(changer, R_AUTOCHANGER) {
51 DEVRES *device;
52 foreach_alist(device, changer->device) {
53 /*
54 * If the device does not have a changer name or changer command
55 * defined, used the one from the Autochanger resource
56 */
57 if (!device->changer_name && changer->changer_name) {
58 device->changer_name = bstrdup(changer->changer_name);
59 }
60 if (!device->changer_command && changer->changer_command) {
61 device->changer_command = bstrdup(changer->changer_command);
62 }
63 if (!device->lock_command && changer->lock_command) {
64 device->lock_command = bstrdup(changer->lock_command);
65 }
66 if (!device->changer_name) {
67 Jmsg(NULL, M_ERROR, 0,
68 _("No Changer Name given for device %s. Cannot continue.\n"),
69 device->hdr.name);
70 OK = false;
71 }
72 if (!device->changer_command) {
73 Jmsg(NULL, M_ERROR, 0,
74 _("No Changer Command given for device %s. Cannot continue.\n"),
75 device->hdr.name);
76 OK = false;
77 }
78 }
79 }
80 return OK;
81 }
82
83
84 /*
85 * Called here to do an autoload using the autochanger, if
86 * configured, and if a Slot has been defined for this Volume.
87 * On success this routine loads the indicated tape, but the
88 * label is not read, so it must be verified.
89 *
90 * Note if dir is not NULL, it is the console requesting the
91 * autoload for labeling, so we respond directly to the
92 * dir bsock.
93 *
94 * Returns: 1 on success
95 * 0 on failure (no changer available)
96 * -1 on error on autochanger
97 */
autoload_device(DCR * dcr,bool writing,BSOCK * dir)98 int autoload_device(DCR *dcr, bool writing, BSOCK *dir)
99 {
100 JCR *jcr = dcr->jcr;
101 DEVICE * volatile dev = dcr->dev;
102 char *new_vol_name = dcr->VolumeName;
103 int slot;
104 int drive = dev->drive_index;
105 int rtn_stat = -1; /* error status */
106 POOLMEM *changer;
107
108 if (!dev->is_autochanger()) {
109 Dmsg1(dbglvl, "Device %s is not an autochanger\n", dev->print_name());
110 return 0;
111 }
112
113 /* An empty ChangerCommand => virtual disk autochanger */
114 if (dcr->is_virtual_autochanger()) {
115 Dmsg0(dbglvl, "ChangerCommand=0, virtual disk changer\n");
116 return 1; /* nothing to load */
117 }
118
119 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
120 /*
121 * Handle autoloaders here. If we cannot autoload it, we
122 * will return 0 so that the sysop will be asked to load it.
123 */
124 if (writing && slot <= 0) {
125 if (dir) {
126 return 0; /* For user, bail out right now */
127 }
128 /* ***FIXME*** this really should not be here */
129 if (dir_find_next_appendable_volume(dcr)) {
130 slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
131 } else {
132 slot = 0;
133 dev->clear_wait();
134 }
135 }
136 Dmsg4(dbglvl, "Want slot=%d drive=%d InChgr=%d Vol=%s\n",
137 dcr->VolCatInfo.Slot, drive,
138 dcr->VolCatInfo.InChanger, dcr->getVolCatName());
139
140 changer = get_pool_memory(PM_FNAME);
141 if (slot <= 0) {
142 /* Suppress info when polling */
143 if (!dev->poll) {
144 Jmsg(jcr, M_INFO, 0, _("No slot defined in catalog (slot=%d) for Volume \"%s\" on %s.\n"),
145 slot, dcr->getVolCatName(), dev->print_name());
146 Jmsg(jcr, M_INFO, 0, _("Cartridge change or \"update slots\" may be required.\n"));
147 }
148 rtn_stat = 0;
149 } else if (!dcr->device->changer_name) {
150 /* Suppress info when polling */
151 if (!dev->poll) {
152 Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" for %s. Manual load of Volume may be required.\n"),
153 dev->print_name());
154 }
155 rtn_stat = 0;
156 } else if (!dcr->device->changer_command) {
157 /* Suppress info when polling */
158 if (!dev->poll) {
159 Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" for %s. Manual load of Volume may be required.\n"),
160 dev->print_name());
161 }
162 rtn_stat = 0;
163 } else {
164 /* Attempt to load the Volume */
165 uint32_t timeout = dcr->device->max_changer_wait;
166 int loaded, status;
167
168 loaded = get_autochanger_loaded_slot(dcr);
169 if (loaded < 0) { /* Autochanger error, try again */
170 loaded = get_autochanger_loaded_slot(dcr);
171 }
172 Dmsg2(dbglvl, "Found loaded=%d drive=%d\n", loaded, drive);
173
174 if (loaded <= 0 || loaded != slot) {
175 POOL_MEM results(PM_MESSAGE);
176
177 /* Unload anything in our drive */
178 if (!unload_autochanger(dcr, loaded)) {
179 goto bail_out;
180 }
181
182 /* Make sure desired slot is unloaded */
183 if (!unload_other_drive(dcr, slot, writing)) {
184 goto bail_out;
185 }
186
187 /*
188 * Load the desired cassette
189 */
190 lock_changer(dcr);
191 Dmsg2(dbglvl, "Doing changer load slot %d %s\n", slot, dev->print_name());
192 Jmsg(jcr, M_INFO, 0,
193 _("3304 Issuing autochanger \"load Volume %s, Slot %d, Drive %d\" command.\n"),
194 new_vol_name, slot, drive);
195 Dmsg3(dbglvl,
196 "3304 Issuing autochanger \"load Volume %s, Slot %d, Drive %d\" command.\n",
197 new_vol_name, slot, drive);
198
199 dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
200 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
201 dev->close(dcr);
202 Dmsg1(dbglvl, "Run program=%s\n", changer);
203 status = run_program_full_output(changer, timeout, results.addr());
204 if (status == 0) {
205 Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load Volume %s, Slot %d, Drive %d\", status is OK.\n"),
206 new_vol_name, slot, drive);
207 Dmsg3(dbglvl, "OK: load volume %s, slot %d, drive %d.\n", new_vol_name, slot, drive);
208 bstrncpy(dev->LoadedVolName, new_vol_name, sizeof(dev->LoadedVolName));
209 dev->set_slot(slot); /* set currently loaded slot */
210 if (dev->vol) {
211 /* We just swapped this Volume so it cannot be swapping any more */
212 dev->vol->clear_swapping();
213 }
214 } else {
215 berrno be;
216 be.set_errno(status);
217 Dmsg5(dbglvl, "Error: load Volume %s, Slot %d, Drive %d, bad stats=%s.\nResults=%s\n",
218 new_vol_name, slot, drive,
219 be.bstrerror(), results.c_str());
220 Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load Volume %s Slot %d, Drive %d\": "
221 "ERR=%s.\nResults=%s\n"),
222 new_vol_name, slot, drive, be.bstrerror(), results.c_str());
223 rtn_stat = -1; /* hard error */
224 dev->clear_slot(); /* mark unknown */
225 }
226 unlock_changer(dcr);
227 } else {
228 status = 0; /* we got what we want */
229 dev->set_slot(slot); /* set currently loaded slot */
230 bstrncpy(dev->LoadedVolName, new_vol_name, sizeof(dev->LoadedVolName));
231 }
232 Dmsg1(dbglvl, "After changer, status=%d\n", status);
233 if (status == 0) { /* did we succeed? */
234 rtn_stat = 1; /* tape loaded by changer */
235 }
236 }
237 free_pool_memory(changer);
238 return rtn_stat;
239
240 bail_out:
241 free_pool_memory(changer);
242 return -1;
243
244 }
245
246 /*
247 * Returns: -1 if error from changer command
248 * slot otherwise
249 * Note, this is safe to do without releasing the drive
250 * since it does not attempt load/unload a slot.
251 */
get_autochanger_loaded_slot(DCR * dcr)252 int get_autochanger_loaded_slot(DCR *dcr)
253 {
254 JCR *jcr = dcr->jcr;
255 DEVICE *dev = dcr->dev;
256 int status, loaded;
257 uint32_t timeout = dcr->device->max_changer_wait;
258 int drive = dcr->dev->drive_index;
259 POOL_MEM results(PM_MESSAGE);
260 POOLMEM *changer;
261
262 if (!dev->is_autochanger()) {
263 return -1;
264 }
265 if (!dcr->device->changer_command) {
266 return -1;
267 }
268
269 if (dev->get_slot() > 0 && dev->has_cap(CAP_ALWAYSOPEN)) {
270 Dmsg1(dbglvl, "Return cached slot=%d\n", dev->get_slot());
271 return dev->get_slot();
272 }
273
274 /* Virtual disk autochanger */
275 if (dcr->is_virtual_autochanger()) {
276 return 1;
277 }
278
279 /* Find out what is loaded, zero means device is unloaded */
280 changer = get_pool_memory(PM_FNAME);
281 lock_changer(dcr);
282 /* Suppress info when polling */
283 if (!dev->poll && chk_dbglvl(1)) {
284 Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
285 drive);
286 }
287 changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
288 Dmsg1(dbglvl, "Run program=%s\n", changer);
289 status = run_program_full_output(changer, timeout, results.addr());
290 Dmsg3(dbglvl, "run_prog: %s stat=%d result=%s", changer, status, results.c_str());
291 if (status == 0) {
292 loaded = str_to_int32(results.c_str());
293 if (loaded > 0) {
294 /* Suppress info when polling */
295 if (!dev->poll && chk_dbglvl(1)) {
296 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
297 drive, loaded);
298 }
299 dev->set_slot(loaded);
300 } else {
301 /* Suppress info when polling */
302 if (!dev->poll && chk_dbglvl(1)) {
303 Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
304 drive);
305 }
306 if (loaded == 0) { /* no slot loaded */
307 dev->set_slot(0);
308 } else { /* probably some error */
309 dev->clear_slot(); /* unknown */
310 }
311 }
312 } else {
313 berrno be;
314 be.set_errno(status);
315 Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: "
316 "ERR=%s.\nResults=%s\n"), drive, be.bstrerror(), results.c_str());
317 Dmsg3(dbglvl, "Error: autochanger loaded? drive %d "
318 "ERR=%s.\nResults=%s\n", drive, be.bstrerror(), results.c_str());
319 loaded = -1; /* force unload */
320 dev->clear_slot(); /* slot unknown */
321 }
322 unlock_changer(dcr);
323 free_pool_memory(changer);
324 return loaded;
325 }
326
lock_changer(DCR * dcr)327 static void lock_changer(DCR *dcr)
328 {
329 AUTOCHANGER *changer_res = dcr->device->changer_res;
330 if (changer_res) {
331 int errstat;
332 Dmsg1(dbglvl, "Locking changer %s\n", changer_res->hdr.name);
333 if ((errstat=rwl_writelock(&changer_res->changer_lock)) != 0) {
334 berrno be;
335 Jmsg(dcr->jcr, M_ERROR_TERM, 0, _("Lock failure on autochanger. ERR=%s\n"),
336 be.bstrerror(errstat));
337 }
338 }
339 }
340
unlock_changer(DCR * dcr)341 static void unlock_changer(DCR *dcr)
342 {
343 AUTOCHANGER *changer_res = dcr->device->changer_res;
344 if (changer_res) {
345 int errstat;
346 Dmsg1(dbglvl, "Unlocking changer %s\n", changer_res->hdr.name);
347 if ((errstat=rwl_writeunlock(&changer_res->changer_lock)) != 0) {
348 berrno be;
349 Jmsg(dcr->jcr, M_ERROR_TERM, 0, _("Unlock failure on autochanger. ERR=%s\n"),
350 be.bstrerror(errstat));
351 }
352 }
353 }
354
355 /*
356 * Unload the volume, if any, in this drive
357 * On entry: loaded == 0 -- nothing to do
358 * loaded < 0 -- check if anything to do
359 * loaded > 0 -- load slot == loaded
360 */
unload_autochanger(DCR * dcr,int loaded)361 bool unload_autochanger(DCR *dcr, int loaded)
362 {
363 DEVICE *dev = dcr->dev;
364 JCR *jcr = dcr->jcr;
365 const char *old_vol_name;
366 int slot;
367 uint32_t timeout = dcr->device->max_changer_wait;
368 bool ok = true;
369
370 if (loaded == 0) {
371 return true;
372 }
373
374 if (!dev->is_autochanger() || !dcr->device->changer_name ||
375 !dcr->device->changer_command) {
376 return false;
377 }
378
379 /* Virtual disk autochanger */
380 if (dcr->is_virtual_autochanger()) {
381 dev->clear_unload();
382 return true;
383 }
384
385 lock_changer(dcr);
386 if (dev->LoadedVolName[0]) {
387 old_vol_name = dev->LoadedVolName;
388 } else {
389 old_vol_name = "*Unknown*";
390 }
391 if (loaded < 0) {
392 loaded = get_autochanger_loaded_slot(dcr);
393 if (loaded < 0) { /* try again, maybe autochanger error */
394 loaded = get_autochanger_loaded_slot(dcr);
395 }
396 }
397
398 if (loaded > 0) {
399 POOL_MEM results(PM_MESSAGE);
400 POOLMEM *changer = get_pool_memory(PM_FNAME);
401 Jmsg(jcr, M_INFO, 0,
402 _("3307 Issuing autochanger \"unload Volume %s, Slot %d, Drive %d\" command.\n"),
403 old_vol_name, loaded, dev->drive_index);
404 Dmsg3(dbglvl,
405 "3307 Issuing autochanger \"unload Volume %s, Slot %d, Drive %d\" command.\n",
406 old_vol_name, loaded, dev->drive_index);
407 slot = dcr->VolCatInfo.Slot;
408 dcr->VolCatInfo.Slot = loaded;
409 changer = edit_device_codes(dcr, changer,
410 dcr->device->changer_command, "unload");
411 dev->close(dcr);
412 Dmsg1(dbglvl, "Run program=%s\n", changer);
413 int stat = run_program_full_output(changer, timeout, results.addr());
414 dcr->VolCatInfo.Slot = slot;
415 if (stat != 0) {
416 berrno be;
417 be.set_errno(stat);
418 Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload Volume %s, Slot %d, Drive %d\": "
419 "ERR=%s\nResults=%s\n"),
420 old_vol_name, loaded, dev->drive_index, be.bstrerror(), results.c_str());
421 Dmsg5(dbglvl, "Error: unload Volume %s, Slot %d, Drive %d, bad stats=%s.\nResults=%s\n",
422 old_vol_name, loaded, dev->drive_index,
423 be.bstrerror(), results.c_str());
424 ok = false;
425 dev->clear_slot(); /* unknown */
426 } else {
427 dev->set_slot(0); /* unload is OK, mark nothing loaded */
428 dev->clear_unload();
429 dev->LoadedVolName[0] = 0; /* clear loaded volume name */
430 }
431 free_pool_memory(changer);
432 }
433 unlock_changer(dcr);
434
435 if (ok) {
436 free_volume(dev);
437 }
438 return ok;
439 }
440
441 /*
442 * Unload the slot if mounted in a different drive
443 */
unload_other_drive(DCR * dcr,int slot,bool writing)444 static bool unload_other_drive(DCR *dcr, int slot, bool writing)
445 {
446 DEVICE *dev = NULL;
447 DEVICE *dev_save;
448 bool found = false;
449 AUTOCHANGER *changer = dcr->dev->device->changer_res;
450 DEVRES *device;
451 int retries = 0; /* wait for device retries */
452 int loaded;
453 int i;
454
455 if (!changer || !changer->device) {
456 return false;
457 }
458 if (changer->device->size() == 1) {
459 return true;
460 }
461
462 /*
463 * We look for the slot number corresponding to the tape
464 * we want in other drives, and if possible, unload
465 * it.
466 */
467 Dmsg1(dbglvl, "Begin wiffle through devices looking for slot=%d\n", slot);
468 /*
469 * foreach_alist(device, changer->device) {
470 *
471 * The above fails to loop through all devices. It is
472 * probably a compiler bug.
473 */
474 for (i=0; i < changer->device->size(); i++) {
475 device = (DEVRES *)changer->device->get(i);
476 dev = device->dev;
477 if (!dev) {
478 Dmsg0(dbglvl, "No dev attached to device\n");
479 continue;
480 }
481
482 dev_save = dcr->dev;
483 dcr->set_dev(dev);
484 loaded = get_autochanger_loaded_slot(dcr);
485 dcr->set_dev(dev_save);
486
487 if (loaded > 0) {
488 Dmsg4(dbglvl, "Want slot=%d, drive=%d loaded=%d dev=%s\n",
489 slot, dev->drive_index, loaded, dev->print_name());
490 if (loaded == slot) {
491 found = true;
492 break;
493 }
494 } else {
495 Dmsg4(dbglvl, "After slot=%d drive=%d loaded=%d dev=%s\n",
496 slot, dev->drive_index, loaded, dev->print_name());
497 }
498 }
499 Dmsg1(dbglvl, "End wiffle through devices looking for slot=%d\n", slot);
500 if (!found) {
501 Dmsg1(dbglvl, "Slot=%d not found in another device\n", slot);
502 return true;
503 } else {
504 Dmsg3(dbglvl, "Slot=%d drive=%d found in dev=%s\n", slot, dev->drive_index, dev->print_name());
505 }
506
507 /*
508 * The Volume we want is on another device.
509 * If we want the Volume to read it, and the other device where the
510 * Volume is currently is not open, we simply unload the Volume then
511 * then the subsequent code will load it in the desired drive.
512 * If want to write or the device is open, we attempt to wait for
513 * the Volume to become available.
514 */
515 if (writing || dev->is_open()) {
516 if (dev->is_busy()) {
517 Dmsg4(dbglvl, "Vol %s for dev=%s in use dev=%s slot=%d\n",
518 dcr->VolumeName, dcr->dev->print_name(),
519 dev->print_name(), slot);
520 }
521 for (int i=0; i < 3; i++) {
522 if (dev->is_busy()) {
523 Dmsg0(40, "Device is busy. Calling wait_for_device()\n");
524 wait_for_device(dcr, retries);
525 continue;
526 }
527 break;
528 }
529 if (dev->is_busy()) {
530 Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" wanted on %s is in use by device %s\n"),
531 dcr->VolumeName, dcr->dev->print_name(), dev->print_name());
532 Dmsg4(dbglvl, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
533 dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->get_slot());
534 Dmsg2(dbglvl, "num_writ=%d reserv=%d\n", dev->num_writers, dev->num_reserved());
535 volume_unused(dcr);
536 return false;
537 }
538 }
539 return unload_dev(dcr, dev);
540 }
541
542 /*
543 * Unconditionally unload a specified drive
544 */
unload_dev(DCR * dcr,DEVICE * dev)545 bool unload_dev(DCR *dcr, DEVICE *dev)
546 {
547 JCR *jcr = dcr->jcr;
548 bool ok = true;
549 uint32_t timeout = dcr->device->max_changer_wait;
550 AUTOCHANGER *changer = dcr->dev->device->changer_res;
551 const char *old_vol_name = dcr->VolumeName;
552 DEVICE *save_dev;
553 int save_slot;
554
555 if (!changer) {
556 return false;
557 }
558
559 save_dev = dcr->dev; /* save dcr device */
560 dcr->set_dev(dev); /* temporarily point dcr at other device */
561
562 get_autochanger_loaded_slot(dcr);
563
564 /* Fail if we have no slot to unload */
565 if (dev->get_slot() <= 0) {
566 if (dev->get_slot() < 0) {
567 Dmsg1(dbglvl, "Cannot unload, slot not defined. dev=%s\n",
568 dev->print_name());
569 }
570 dcr->set_dev(save_dev);
571 return false;
572 }
573
574 save_slot = dcr->VolCatInfo.Slot;
575 dcr->VolCatInfo.Slot = dev->get_slot();
576
577 POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
578 POOL_MEM results(PM_MESSAGE);
579 if (old_vol_name[0] == 0) {
580 if (dev->LoadedVolName[0]) {
581 old_vol_name = dev->LoadedVolName;
582 } else {
583 old_vol_name = "*Unknown*";
584 }
585 }
586 lock_changer(dcr);
587 Jmsg(jcr, M_INFO, 0,
588 _("3307 Issuing autochanger \"unload Volume %s, Slot %d, Drive %d\" command.\n"),
589 old_vol_name, dev->get_slot(), dev->drive_index);
590 Dmsg3(0/*dbglvl*/, "Issuing autochanger \"unload Volume %s, Slot %d, Drive %d\" command.\n",
591 old_vol_name, dev->get_slot(), dev->drive_index);
592
593 changer_cmd = edit_device_codes(dcr, changer_cmd,
594 dcr->device->changer_command, "unload");
595 dev->close(dcr);
596 Dmsg2(dbglvl, "close dev=%s reserve=%d\n", dev->print_name(),
597 dev->num_reserved());
598 Dmsg1(dbglvl, "Run program=%s\n", changer_cmd);
599 int stat = run_program_full_output(changer_cmd, timeout, results.addr());
600 dcr->VolCatInfo.Slot = save_slot;
601 if (stat != 0) {
602 berrno be;
603 be.set_errno(stat);
604 Jmsg(jcr, M_INFO, 0, _("3997 Bad autochanger \"unload Volume %s, Slot %d, Drive %d\": ERR=%s.\n"),
605 old_vol_name, dev->get_slot(), dev->drive_index, be.bstrerror());
606 Dmsg5(dbglvl, "Error: unload Volume %s, Slot %d, Drive %d bad stats=%s.\nResults=%s\n",
607 old_vol_name, dev->get_slot(), dev->drive_index,
608 be.bstrerror(), results.c_str());
609 ok = false;
610 dev->clear_slot(); /* unknown */
611 } else {
612 Dmsg3(dbglvl, "Volume %s, Slot %d unloaded %s\n",
613 old_vol_name, dev->get_slot(), dev->print_name());
614 dev->set_slot(0); /* unload OK, mark nothing loaded */
615 dev->clear_unload();
616 dev->LoadedVolName[0] = 0;
617 }
618 unlock_changer(dcr);
619
620 if (ok) {
621 free_volume(dev);
622 }
623 dcr->set_dev(save_dev);
624 free_pool_memory(changer_cmd);
625 return ok;
626 }
627
628
629
630 /*
631 * List the Volumes that are in the autoloader possibly
632 * with their barcodes.
633 * We assume that it is always the Console that is calling us.
634 */
autochanger_cmd(DCR * dcr,BSOCK * dir,const char * cmd)635 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)
636 {
637 DEVICE *dev = dcr->dev;
638 uint32_t timeout = dcr->device->max_changer_wait;
639 POOLMEM *changer;
640 BPIPE *bpipe;
641 int len = sizeof_pool_memory(dir->msg) - 1;
642 int stat;
643
644 if (!dev->is_autochanger() || !dcr->device->changer_name ||
645 !dcr->device->changer_command) {
646 if (strcasecmp(cmd, "drives") == 0) {
647 dir->fsend("drives=1\n");
648 }
649 dir->fsend(_("3993 Device %s not an autochanger device.\n"),
650 dev->print_name());
651 return false;
652 }
653
654 if (strcasecmp(cmd, "drives") == 0) {
655 AUTOCHANGER *changer_res = dcr->device->changer_res;
656 int drives = 1;
657 if (changer_res && changer_res->device) {
658 drives = changer_res->device->size();
659 }
660 dir->fsend("drives=%d\n", drives);
661 Dmsg1(dbglvl, "drives=%d\n", drives);
662 return true;
663 }
664
665 /* If listing, reprobe changer */
666 if (bstrcasecmp(cmd, "list") || bstrcasecmp(cmd, "listall")) {
667 dcr->dev->set_slot(0);
668 get_autochanger_loaded_slot(dcr);
669 }
670
671 changer = get_pool_memory(PM_FNAME);
672 lock_changer(dcr);
673 /* Now issue the command */
674 changer = edit_device_codes(dcr, changer,
675 dcr->device->changer_command, cmd);
676 dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd);
677 bpipe = open_bpipe(changer, timeout, "r");
678 if (!bpipe) {
679 dir->fsend(_("3996 Open bpipe to changer failed: %s.\n"), changer);
680 goto bail_out; /* TODO: check if we need to return false */
681 }
682 if (bstrcasecmp(cmd, "list") || bstrcasecmp(cmd, "listall")) {
683 /* Get output from changer */
684 while (fgets(dir->msg, len, bpipe->rfd)) {
685 dir->msglen = strlen(dir->msg);
686 Dmsg1(dbglvl, "<stored: %s\n", dir->msg);
687 dir->send();
688 }
689 } else if (strcasecmp(cmd, "slots") == 0 ) {
690 char buf[100], *p;
691 /* For slots command, read a single line */
692 buf[0] = 0;
693 fgets(buf, sizeof(buf)-1, bpipe->rfd);
694 buf[sizeof(buf)-1] = 0;
695 /* Strip any leading space in front of # of slots */
696 for (p=buf; B_ISSPACE(*p); p++)
697 { }
698 dir->fsend("slots=%s", p);
699 Dmsg1(dbglvl, "<stored: %s", dir->msg);
700 }
701
702 stat = close_bpipe(bpipe);
703 if (stat != 0) {
704 berrno be;
705 be.set_errno(stat);
706 dir->fsend(_("Autochanger error: ERR=%s\n"), be.bstrerror());
707 }
708
709 bail_out:
710 unlock_changer(dcr);
711 free_pool_memory(changer);
712 return true;
713 }
714
715
716 /*
717 * Edit codes into ChangerCommand
718 * %% = %
719 * %a = archive device name
720 * %c = changer device name
721 * %d = changer drive index
722 * %f = Client's name
723 * %j = Job name
724 * %l = archive control channel name
725 * %o = command
726 * %s = Slot base 0
727 * %S = Slot base 1
728 * %v = Volume name
729 *
730 *
731 * omsg = edited output message
732 * imsg = input string containing edit codes (%x)
733 * cmd = command string (load, unload, ...)
734 *
735 */
edit_device_codes(DCR * dcr,char * omsg,const char * imsg,const char * cmd)736 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
737 {
738 const char *p;
739 const char *str;
740 char add[20];
741
742 *omsg = 0;
743 Dmsg1(1800, "edit_device_codes: %s\n", imsg);
744 for (p=imsg; *p; p++) {
745 if (*p == '%') {
746 switch (*++p) {
747 case '%':
748 str = "%";
749 break;
750 case 'a':
751 str = dcr->dev->archive_name();
752 break;
753 case 'c':
754 str = NPRT(dcr->device->changer_name);
755 break;
756 case 'l':
757 str = NPRT(dcr->device->control_name);
758 break;
759 case 'd':
760 sprintf(add, "%d", dcr->dev->drive_index);
761 str = add;
762 break;
763 case 'o':
764 str = NPRT(cmd);
765 break;
766 case 's':
767 sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
768 str = add;
769 break;
770 case 'S':
771 sprintf(add, "%d", dcr->VolCatInfo.Slot);
772 str = add;
773 break;
774 case 'j': /* Job name */
775 str = dcr->jcr->Job;
776 break;
777 case 'v':
778 if (dcr->dev->LoadedVolName[0]) {
779 str = dcr->dev->LoadedVolName;
780 } else if (dcr->VolCatInfo.VolCatName[0]) {
781 str = dcr->VolCatInfo.VolCatName;
782 } else if (dcr->VolumeName[0]) {
783 str = dcr->VolumeName;
784 } else if (dcr->dev->vol && dcr->dev->vol->vol_name) {
785 str = dcr->dev->vol->vol_name;
786 } else {
787 str = dcr->dev->VolHdr.VolumeName;
788 }
789 break;
790 case 'f':
791 str = NPRT(dcr->jcr->client_name);
792 break;
793
794 default:
795 add[0] = '%';
796 add[1] = *p;
797 add[2] = 0;
798 str = add;
799 break;
800 }
801 } else {
802 add[0] = *p;
803 add[1] = 0;
804 str = add;
805 }
806 Dmsg1(1900, "add_str %s\n", str);
807 pm_strcat(&omsg, (char *)str);
808 Dmsg1(1800, "omsg=%s\n", omsg);
809 }
810 Dmsg1(800, "omsg=%s\n", omsg);
811 return omsg;
812 }
813