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