]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
Rename incomplete to rerunning for clarity
[bacula/bacula] / bacula / src / stored / autochanger.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *  Routines for handling the autochanger.
31  *
32  *   Kern Sibbald, August MMII
33  *                            
34  */
35
36 #include "bacula.h"                   /* pull in global headers */
37 #include "stored.h"                   /* pull in Storage Deamon headers */
38
39 /* Forward referenced functions */
40 static void lock_changer(DCR *dcr);
41 static void unlock_changer(DCR *dcr);
42 static bool unload_other_drive(DCR *dcr, int slot);
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 #ifdef xxx_needed
77          if (media_type == NULL) {
78             media_type = device->media_type;     /* get Media Type of first device */
79             continue;
80          }     
81          /* Ensure that other devices Media Types are the same */
82          if (strcmp(media_type, device->media_type) != 0) {
83             Jmsg(NULL, M_ERROR, 0, 
84                _("Media Type not the same for all devices in changer %s. Cannot continue.\n"),
85                changer->hdr.name);
86             OK = false;
87             continue;
88          }
89 #endif
90       }
91    }
92    return OK;
93 }
94
95
96 /*
97  * Called here to do an autoload using the autochanger, if
98  *  configured, and if a Slot has been defined for this Volume.
99  *  On success this routine loads the indicated tape, but the
100  *  label is not read, so it must be verified.
101  *
102  *  Note if dir is not NULL, it is the console requesting the
103  *   autoload for labeling, so we respond directly to the
104  *   dir bsock.
105  *
106  *  Returns: 1 on success
107  *           0 on failure (no changer available)
108  *          -1 on error on autochanger
109  */
110 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
111 {
112    JCR *jcr = dcr->jcr;
113    DEVICE * volatile dev = dcr->dev;
114    int slot;
115    int drive = dev->drive_index;
116    int rtn_stat = -1;                 /* error status */
117    POOLMEM *changer;
118
119    if (!dev->is_autochanger()) {
120       Dmsg1(100, "Device %s is not an autochanger\n", dev->print_name());
121       return 0;
122    }
123
124    /* An empty ChangerCommand => virtual disk autochanger */
125    if (dcr->device->changer_command && dcr->device->changer_command[0] == 0) {
126       Dmsg0(100, "ChangerCommand=0, virtual disk changer\n");
127       return 1;                       /* nothing to load */
128    }
129
130    slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
131    Dmsg3(100, "autoload: slot=%d InChgr=%d Vol=%s\n", dcr->VolCatInfo.Slot,
132          dcr->VolCatInfo.InChanger, dcr->getVolCatName());
133    /*
134     * Handle autoloaders here.  If we cannot autoload it, we
135     *  will return 0 so that the sysop will be asked to load it.
136     */
137    if (writing && slot <= 0) {
138       if (dir) {
139          return 0;                    /* For user, bail out right now */
140       }
141       /* ***FIXME*** this really should not be here */
142       if (dir_find_next_appendable_volume(dcr)) {
143          slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
144       } else {
145          slot = 0;
146       }
147    }
148    Dmsg1(400, "Want changer slot=%d\n", slot);
149
150    changer = get_pool_memory(PM_FNAME);
151    if (slot <= 0) {
152       /* Suppress info when polling */
153       if (!dev->poll) {
154          Jmsg(jcr, M_INFO, 0, _("No slot defined in catalog (slot=%d) for Volume \"%s\" on %s.\n"), 
155               slot, dcr->getVolCatName(), dev->print_name());
156          Jmsg(jcr, M_INFO, 0, _("Cartridge change or \"update slots\" may be required.\n"));
157       }
158       rtn_stat = 0;
159    } else if (!dcr->device->changer_name) {
160       /* Suppress info when polling */
161       if (!dev->poll) {
162          Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" for %s. Manual load of Volume may be required.\n"),
163               dev->print_name());
164       }
165       rtn_stat = 0;
166   } else if (!dcr->device->changer_command) {
167       /* Suppress info when polling */
168       if (!dev->poll) {
169          Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" for %s. Manual load of Volume may be requird.\n"),
170               dev->print_name());
171       }
172       rtn_stat = 0;
173   } else {
174       /* Attempt to load the Volume */
175
176       uint32_t timeout = dcr->device->max_changer_wait;
177       int loaded, status;
178
179       loaded = get_autochanger_loaded_slot(dcr);
180
181       if (loaded != slot) {
182          POOL_MEM results(PM_MESSAGE);
183
184          /* Unload anything in our drive */
185          if (!unload_autochanger(dcr, loaded)) {
186             goto bail_out;
187          }
188             
189          /* Make sure desired slot is unloaded */
190          if (!unload_other_drive(dcr, slot)) {
191             goto bail_out;
192          }
193
194          /*
195           * Load the desired cassette
196           */
197          lock_changer(dcr);
198          Dmsg2(100, "Doing changer load slot %d %s\n", slot, dev->print_name());
199          Jmsg(jcr, M_INFO, 0,
200               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
201               slot, drive);
202          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
203          changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
204          dev->close();
205          Dmsg1(200, "Run program=%s\n", changer);
206          status = run_program_full_output(changer, timeout, results.addr());
207          if (status == 0) {
208             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
209                     slot, drive);
210             Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
211             dev->set_slot(slot);      /* set currently loaded slot */
212          } else {
213             berrno be;
214             be.set_errno(status);
215             Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
216                be.bstrerror());
217             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": "
218                  "ERR=%s.\nResults=%s\n"),
219                     slot, drive, be.bstrerror(), results.c_str());
220             rtn_stat = -1;            /* hard error */
221             dev->set_slot(-1);        /* mark unknown */
222          }
223          Dmsg2(100, "load slot %d status=%d\n", slot, status);
224          unlock_changer(dcr);
225       } else {
226          status = 0;                  /* we got what we want */
227          dev->set_slot(slot);         /* set currently loaded slot */
228       }
229       Dmsg1(100, "After changer, status=%d\n", status);
230       if (status == 0) {              /* did we succeed? */
231          rtn_stat = 1;                /* tape loaded by changer */
232       }
233    }
234    free_pool_memory(changer);
235    return rtn_stat;
236
237 bail_out:
238    free_pool_memory(changer);
239    return -1;
240
241 }
242
243 /*
244  * Returns: -1 if error from changer command
245  *          slot otherwise
246  *  Note, this is safe to do without releasing the drive
247  *   since it does not attempt load/unload a slot.
248  */
249 int get_autochanger_loaded_slot(DCR *dcr)
250 {
251    JCR *jcr = dcr->jcr;
252    DEVICE *dev = dcr->dev;
253    int status, loaded;
254    uint32_t timeout = dcr->device->max_changer_wait;
255    int drive = dcr->dev->drive_index;
256    POOL_MEM results(PM_MESSAGE);
257    POOLMEM *changer;
258
259    if (!dev->is_autochanger()) {
260       return -1;
261    }
262    if (!dcr->device->changer_command) {
263 //    Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
264       return -1;
265    }
266    if (dev->get_slot() > 0) {
267       return dev->get_slot();
268    }
269
270    /* Virtual disk autochanger */
271    if (dcr->device->changer_command[0] == 0) {
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) {
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(100, "Run program=%s\n", changer);
285    status = run_program_full_output(changer, timeout, results.addr());
286    Dmsg3(100, "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) {
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) {
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       loaded = -1;              /* force unload */
314    }
315    unlock_changer(dcr);
316    free_pool_memory(changer);
317    return loaded;
318 }
319
320 static void lock_changer(DCR *dcr)
321 {
322    AUTOCHANGER *changer_res = dcr->device->changer_res;
323    if (changer_res) {
324       Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
325       P(changer_res->changer_mutex);  /* Lock changer script */
326    }
327 }
328
329 static void unlock_changer(DCR *dcr)
330 {
331    AUTOCHANGER *changer_res = dcr->device->changer_res;
332    if (changer_res) {
333       Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
334       V(changer_res->changer_mutex);  /* Unlock changer script */
335    }
336 }
337
338 /*
339  * Unload the volume, if any, in this drive
340  *  On entry: loaded == 0 -- nothing to do
341  *            loaded  < 0 -- check if anything to do
342  *            loaded  > 0 -- load slot == loaded
343  */
344 bool unload_autochanger(DCR *dcr, int loaded)
345 {
346    DEVICE *dev = dcr->dev;
347    JCR *jcr = dcr->jcr;
348    int slot;
349    uint32_t timeout = dcr->device->max_changer_wait;
350    bool ok = true;
351
352    if (loaded == 0) {
353       return true;
354    }
355
356    if (!dev->is_autochanger() || !dcr->device->changer_name ||
357        !dcr->device->changer_command) {
358       return false;
359    }
360
361    /* Virtual disk autochanger */
362    if (dcr->device->changer_command[0] == 0) {
363       dev->clear_unload();
364       return true;
365    }
366
367    if (loaded < 0) {
368       loaded = get_autochanger_loaded_slot(dcr);
369    }
370
371    if (loaded > 0) {
372       POOL_MEM results(PM_MESSAGE);
373       POOLMEM *changer = get_pool_memory(PM_FNAME);
374       lock_changer(dcr);
375       Jmsg(jcr, M_INFO, 0,
376            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
377            loaded, dev->drive_index);
378       slot = dcr->VolCatInfo.Slot;
379       dcr->VolCatInfo.Slot = loaded;
380       changer = edit_device_codes(dcr, changer, 
381                    dcr->device->changer_command, "unload");
382       dev->close();
383       Dmsg1(100, "Run program=%s\n", changer);
384       int stat = run_program_full_output(changer, timeout, results.addr());
385       dcr->VolCatInfo.Slot = slot;
386       if (stat != 0) {
387          berrno be;
388          be.set_errno(stat);
389          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": "
390               "ERR=%s\nResults=%s\n"),
391                  loaded, dev->drive_index, be.bstrerror(), results.c_str());
392          ok = false;
393          dev->clear_slot();        /* unknown */
394       } else {
395          dev->set_slot(0);         /* nothing loaded */
396       }
397       unlock_changer(dcr);
398
399       free_volume(dev);            /* Free any volume associated with this drive */
400       free_pool_memory(changer);
401    }
402    if (ok) {
403       dev->clear_unload();
404    }
405    return ok;
406 }
407
408 /*
409  * Unload the slot if mounted in a different drive
410  */
411 static bool unload_other_drive(DCR *dcr, int slot)
412 {
413    DEVICE *dev = NULL;
414    bool found = false;
415    AUTOCHANGER *changer = dcr->dev->device->changer_res;
416    DEVRES *device;
417    int retries = 0;                /* wait for device retries */
418
419    if (!changer) {
420       return false;
421    }
422    if (changer->device->size() == 1) {
423       return true;
424    }
425
426    foreach_alist(device, changer->device) {
427       if (device->dev && device->dev->get_slot() == slot) {
428          found = true;
429          dev = device->dev;
430          break;
431       }
432    }
433    if (!found) {
434       return true;
435    }
436
437    /* The Volume we want is on another device. */
438    if (dev->is_busy()) {
439       Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
440            dcr->VolumeName, dcr->dev->print_name(),
441            dev->print_name(), slot);
442    }   
443    for (int i=0; i < 3; i++) {
444       if (dev->is_busy()) {
445          wait_for_device(dcr->jcr, retries);
446          continue;
447       }
448       break;
449    }
450    if (dev->is_busy()) {
451       Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" wanted on %s is in use by device %s\n"),
452            dcr->VolumeName, dcr->dev->print_name(), dev->print_name());
453       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
454            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->get_slot());
455       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->num_reserved());
456       volume_unused(dcr);
457       return false;
458    }
459    return unload_dev(dcr, dev);
460 }
461
462 /*
463  * Unconditionally unload a specified drive
464  */
465 bool unload_dev(DCR *dcr, DEVICE *dev)
466 {
467    JCR *jcr = dcr->jcr;
468    bool ok = true;
469    uint32_t timeout = dcr->device->max_changer_wait;
470    AUTOCHANGER *changer = dcr->dev->device->changer_res;
471    DEVICE *save_dev;
472    int save_slot;
473
474    if (!changer) {
475       return false;
476    }
477
478    save_dev = dcr->dev;               /* save dcr device */
479    dcr->dev = dev;                    /* temporarily point dcr at other device */
480
481    if (dev->get_slot() <= 0 && get_autochanger_loaded_slot(dcr) <= 0) {
482       dcr->dev = save_dev;
483       return false;
484    }
485    save_slot = dcr->VolCatInfo.Slot;
486    dcr->VolCatInfo.Slot = dev->get_slot();
487
488 //   dev->dlock();
489
490    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
491    POOL_MEM results(PM_MESSAGE);
492    lock_changer(dcr);
493    Jmsg(jcr, M_INFO, 0,
494         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
495         dev->get_slot(), dev->drive_index);
496
497    Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
498         dev->get_slot(), dev->drive_index);
499
500    changer_cmd = edit_device_codes(dcr, changer_cmd, 
501                 dcr->device->changer_command, "unload");
502    dev->close();
503    Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), 
504       dev->num_reserved());
505    Dmsg1(100, "Run program=%s\n", changer_cmd);
506    int stat = run_program_full_output(changer_cmd, timeout, results.addr());
507    dcr->VolCatInfo.Slot = save_slot;
508    dcr->dev = save_dev;
509    if (stat != 0) {
510       berrno be;
511       be.set_errno(stat);
512       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
513               dev->get_slot(), dev->drive_index, be.bstrerror());
514
515       Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
516               dev->get_slot(), dev->drive_index, be.bstrerror());
517       ok = false;
518       dev->clear_slot();          /* unknown */
519    } else {
520       Dmsg2(100, "Slot %d unloaded %s\n", dev->get_slot(), dev->print_name());
521       dev->set_slot(0);           /* nothing loaded */
522    }
523    if (ok) {
524       dev->clear_unload();
525    }
526    unlock_changer(dcr);
527
528 //   dev->dunlock();
529
530    free_volume(dev);               /* Free any volume associated with this drive */
531    free_pool_memory(changer_cmd);
532    return ok;
533 }
534
535
536
537 /*
538  * List the Volumes that are in the autoloader possibly
539  *   with their barcodes.
540  *   We assume that it is always the Console that is calling us.
541  */
542 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
543 {
544    DEVICE *dev = dcr->dev;
545    uint32_t timeout = dcr->device->max_changer_wait;
546    POOLMEM *changer;
547    BPIPE *bpipe;
548    int len = sizeof_pool_memory(dir->msg) - 1;
549    bool ok = false;
550    int stat;
551
552    if (!dev->is_autochanger() || !dcr->device->changer_name ||
553        !dcr->device->changer_command) {
554       if (strcmp(cmd, "drives") == 0) {
555          dir->fsend("drives=1\n");
556       }
557       dir->fsend(_("3993 Device %s not an autochanger device.\n"),
558          dev->print_name());
559       return false;
560    }
561
562    if (strcmp(cmd, "drives") == 0) {
563       AUTOCHANGER *changer_res = dcr->device->changer_res;
564       int drives = 1;
565       if (changer_res) {
566          drives = changer_res->device->size();
567       }
568       dir->fsend("drives=%d\n", drives);
569       Dmsg1(100, "drives=%d\n", drives);
570       return true;
571    }
572
573    changer = get_pool_memory(PM_FNAME);
574    lock_changer(dcr);
575    /* Now issue the command */
576    changer = edit_device_codes(dcr, changer, 
577                  dcr->device->changer_command, cmd);
578    dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd);
579    bpipe = open_bpipe(changer, timeout, "r");
580    if (!bpipe) {
581       dir->fsend(_("3996 Open bpipe failed.\n"));
582       goto bail_out;
583    }
584    if (bstrcmp(cmd, "list") || bstrcmp(cmd, "listall")) {
585       /* Get output from changer */
586       while (fgets(dir->msg, len, bpipe->rfd)) {
587          dir->msglen = strlen(dir->msg);
588          Dmsg1(100, "<stored: %s\n", dir->msg);
589          bnet_send(dir);
590       }
591    } else if (strcmp(cmd, "slots") == 0 ) {
592       char buf[100], *p;
593       /* For slots command, read a single line */
594       buf[0] = 0;
595       fgets(buf, sizeof(buf)-1, bpipe->rfd);
596       buf[sizeof(buf)-1] = 0;
597       /* Strip any leading space in front of # of slots */
598       for (p=buf; B_ISSPACE(*p); p++)
599         { }
600       dir->fsend("slots=%s", p);
601       Dmsg1(100, "<stored: %s", dir->msg);
602    } 
603                  
604    stat = close_bpipe(bpipe);
605    if (stat != 0) {
606       berrno be;
607       be.set_errno(stat);
608       dir->fsend(_("Autochanger error: ERR=%s\n"), be.bstrerror());
609    }
610    bnet_sig(dir, BNET_EOD);
611    ok = true;
612
613 bail_out:
614    unlock_changer(dcr);
615    free_pool_memory(changer);
616    return true;
617 }
618
619
620 /*
621  * Edit codes into ChangerCommand
622  *  %% = %
623  *  %a = archive device name
624  *  %c = changer device name
625  *  %d = changer drive index
626  *  %f = Client's name
627  *  %j = Job name
628  *  %o = command
629  *  %s = Slot base 0
630  *  %S = Slot base 1
631  *  %v = Volume name
632  *
633  *
634  *  omsg = edited output message
635  *  imsg = input string containing edit codes (%x)
636  *  cmd = command string (load, unload, ...)
637  *
638  */
639 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
640 {
641    const char *p;
642    const char *str;
643    char add[20];
644
645    *omsg = 0;
646    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
647    for (p=imsg; *p; p++) {
648       if (*p == '%') {
649          switch (*++p) {
650          case '%':
651             str = "%";
652             break;
653          case 'a':
654             str = dcr->dev->archive_name();
655             break;
656          case 'c':
657             str = NPRT(dcr->device->changer_name);
658             break;
659          case 'd':
660             sprintf(add, "%d", dcr->dev->drive_index);
661             str = add;
662             break;
663          case 'o':
664             str = NPRT(cmd);
665             break;
666          case 's':
667             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
668             str = add;
669             break;
670          case 'S':
671             sprintf(add, "%d", dcr->VolCatInfo.Slot);
672             str = add;
673             break;
674          case 'j':                    /* Job name */
675             str = dcr->jcr->Job;
676             break;
677          case 'v':
678             if (dcr->VolCatInfo.VolCatName[0]) {
679                str = dcr->VolCatInfo.VolCatName;
680             } else if (dcr->VolumeName[0]) {
681                str = dcr->VolumeName;
682             } else if (dcr->dev->vol && dcr->dev->vol->vol_name) {
683                str = dcr->dev->vol->vol_name;
684             } else {
685                str = dcr->dev->VolHdr.VolumeName;
686             }
687             break;
688          case 'f':
689             str = NPRT(dcr->jcr->client_name);
690             break;
691
692          default:
693             add[0] = '%';
694             add[1] = *p;
695             add[2] = 0;
696             str = add;
697             break;
698          }
699       } else {
700          add[0] = *p;
701          add[1] = 0;
702          str = add;
703       }
704       Dmsg1(1900, "add_str %s\n", str);
705       pm_strcat(&omsg, (char *)str);
706       Dmsg1(1800, "omsg=%s\n", omsg);
707    }
708    Dmsg1(800, "omsg=%s\n", omsg);
709    return omsg;
710 }