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