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