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