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