]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
40189d6ae1d92ef236b82ee65ba923266fe99b8e
[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       Jmsg(jcr, M_INFO, 0, _("Invalid slot=%d defined in catalog for Volume \"%s\" "
153            "on %s. Manual load may be required.\n"), slot, dcr->getVolCatName(),
154            dev->print_name());
155       rtn_stat = 0;
156    } else if (!dcr->device->changer_name) {
157       Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" for %s. Manual load of Volume may be required.\n"),
158            dev->print_name());
159       rtn_stat = 0;
160   } else if (!dcr->device->changer_command) {
161       Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" for %s. Manual load of Volume may be requird.\n"),
162            dev->print_name());
163       rtn_stat = 0;
164   } else {
165       /* Attempt to load the Volume */
166
167       uint32_t timeout = dcr->device->max_changer_wait;
168       int loaded, status;
169
170       loaded = get_autochanger_loaded_slot(dcr);
171
172       if (loaded != slot) {
173          POOL_MEM results(PM_MESSAGE);
174
175          /* Unload anything in our drive */
176          if (!unload_autochanger(dcr, loaded)) {
177             goto bail_out;
178          }
179             
180          /* Make sure desired slot is unloaded */
181          if (!unload_other_drive(dcr, slot)) {
182             goto bail_out;
183          }
184
185          /*
186           * Load the desired cassette
187           */
188          lock_changer(dcr);
189          Dmsg2(100, "Doing changer load slot %d %s\n", slot, dev->print_name());
190          Jmsg(jcr, M_INFO, 0,
191               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
192               slot, drive);
193          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
194          changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
195          dev->close();
196          Dmsg1(200, "Run program=%s\n", changer);
197          status = run_program_full_output(changer, timeout, results.addr());
198          if (status == 0) {
199             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
200                     slot, drive);
201             Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
202             dev->set_slot(slot);      /* set currently loaded slot */
203          } else {
204             berrno be;
205             be.set_errno(status);
206             Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
207                be.bstrerror());
208             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": "
209                  "ERR=%s.\nResults=%s\n"),
210                     slot, drive, be.bstrerror(), results.c_str());
211             rtn_stat = -1;            /* hard error */
212             dev->set_slot(-1);        /* mark unknown */
213          }
214          Dmsg2(100, "load slot %d status=%d\n", slot, status);
215          unlock_changer(dcr);
216       } else {
217          status = 0;                  /* we got what we want */
218          dev->set_slot(slot);         /* set currently loaded slot */
219       }
220       Dmsg1(100, "After changer, status=%d\n", status);
221       if (status == 0) {              /* did we succeed? */
222          rtn_stat = 1;                /* tape loaded by changer */
223       }
224    }
225    free_pool_memory(changer);
226    return rtn_stat;
227
228 bail_out:
229    free_pool_memory(changer);
230    return -1;
231
232 }
233
234 /*
235  * Returns: -1 if error from changer command
236  *          slot otherwise
237  *  Note, this is safe to do without releasing the drive
238  *   since it does not attempt load/unload a slot.
239  */
240 int get_autochanger_loaded_slot(DCR *dcr)
241 {
242    JCR *jcr = dcr->jcr;
243    DEVICE *dev = dcr->dev;
244    int status, loaded;
245    uint32_t timeout = dcr->device->max_changer_wait;
246    int drive = dcr->dev->drive_index;
247    POOL_MEM results(PM_MESSAGE);
248    POOLMEM *changer;
249
250    if (!dev->is_autochanger()) {
251       return -1;
252    }
253    if (!dcr->device->changer_command) {
254 //    Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
255       return -1;
256    }
257    if (dev->get_slot() > 0) {
258       return dev->get_slot();
259    }
260
261    /* Virtual disk autochanger */
262    if (dcr->device->changer_command[0] == 0) {
263       return 1;
264    }
265
266    /* Find out what is loaded, zero means device is unloaded */
267    changer = get_pool_memory(PM_FNAME);
268    lock_changer(dcr);
269    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
270         drive);
271    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
272    Dmsg1(100, "Run program=%s\n", changer);
273    status = run_program_full_output(changer, timeout, results.addr());
274    Dmsg3(100, "run_prog: %s stat=%d result=%s", changer, status, results.c_str());
275    if (status == 0) {
276       loaded = str_to_int32(results.c_str());
277       if (loaded > 0) {
278          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
279               drive, loaded);
280          dev->set_slot(loaded);
281       } else {
282          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
283               drive);
284          dev->clear_slot();   /* unknown */
285       }
286    } else {
287       berrno be;
288       be.set_errno(status);
289       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: "
290            "ERR=%s.\nResults=%s\n"), drive, be.bstrerror(), results.c_str());
291       loaded = -1;              /* force unload */
292    }
293    unlock_changer(dcr);
294    free_pool_memory(changer);
295    return loaded;
296 }
297
298 static void lock_changer(DCR *dcr)
299 {
300    AUTOCHANGER *changer_res = dcr->device->changer_res;
301    if (changer_res) {
302       Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
303       P(changer_res->changer_mutex);  /* Lock changer script */
304    }
305 }
306
307 static void unlock_changer(DCR *dcr)
308 {
309    AUTOCHANGER *changer_res = dcr->device->changer_res;
310    if (changer_res) {
311       Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
312       V(changer_res->changer_mutex);  /* Unlock changer script */
313    }
314 }
315
316 /*
317  * Unload the volume, if any, in this drive
318  *  On entry: loaded == 0 -- nothing to do
319  *            loaded  < 0 -- check if anything to do
320  *            loaded  > 0 -- load slot == loaded
321  */
322 bool unload_autochanger(DCR *dcr, int loaded)
323 {
324    DEVICE *dev = dcr->dev;
325    JCR *jcr = dcr->jcr;
326    int slot;
327    uint32_t timeout = dcr->device->max_changer_wait;
328    bool ok = true;
329
330    if (loaded == 0) {
331       return true;
332    }
333
334    if (!dev->is_autochanger() || !dcr->device->changer_name ||
335        !dcr->device->changer_command) {
336       return false;
337    }
338
339    /* Virtual disk autochanger */
340    if (dcr->device->changer_command[0] == 0) {
341       dev->clear_unload();
342       return true;
343    }
344
345    if (loaded < 0) {
346       loaded = get_autochanger_loaded_slot(dcr);
347    }
348
349    if (loaded > 0) {
350       POOL_MEM results(PM_MESSAGE);
351       POOLMEM *changer = get_pool_memory(PM_FNAME);
352       lock_changer(dcr);
353       Jmsg(jcr, M_INFO, 0,
354            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
355            loaded, dev->drive_index);
356       slot = dcr->VolCatInfo.Slot;
357       dcr->VolCatInfo.Slot = loaded;
358       changer = edit_device_codes(dcr, changer, 
359                    dcr->device->changer_command, "unload");
360       dev->close();
361       Dmsg1(100, "Run program=%s\n", changer);
362       int stat = run_program_full_output(changer, timeout, results.addr());
363       dcr->VolCatInfo.Slot = slot;
364       if (stat != 0) {
365          berrno be;
366          be.set_errno(stat);
367          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": "
368               "ERR=%s\nResults=%s\n"),
369                  loaded, dev->drive_index, be.bstrerror(), results.c_str());
370          ok = false;
371          dev->clear_slot();        /* unknown */
372       } else {
373          dev->set_slot(0);         /* nothing loaded */
374       }
375       unlock_changer(dcr);
376
377       free_volume(dev);            /* Free any volume associated with this drive */
378       free_pool_memory(changer);
379    }
380    if (ok) {
381       dev->clear_unload();
382    }
383    return ok;
384 }
385
386 /*
387  * Unload the slot if mounted in a different drive
388  */
389 static bool unload_other_drive(DCR *dcr, int slot)
390 {
391    DEVICE *dev = NULL;
392    bool found = false;
393    AUTOCHANGER *changer = dcr->dev->device->changer_res;
394    DEVRES *device;
395    int retries = 0;                /* wait for device retries */
396
397    if (!changer) {
398       return false;
399    }
400    if (changer->device->size() == 1) {
401       return true;
402    }
403
404    foreach_alist(device, changer->device) {
405       if (device->dev && device->dev->get_slot() == slot) {
406          found = true;
407          dev = device->dev;
408          break;
409       }
410    }
411    if (!found) {
412       return true;
413    }
414
415    /* The Volume we want is on another device. */
416    if (dev->is_busy()) {
417       Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
418            dcr->VolumeName, dcr->dev->print_name(),
419            dev->print_name(), slot);
420    }   
421    for (int i=0; i < 3; i++) {
422       if (dev->is_busy()) {
423          wait_for_device(dcr->jcr, retries);
424          continue;
425       }
426       break;
427    }
428    if (dev->is_busy()) {
429       Jmsg(dcr->jcr, M_WARNING, 0, _("Volume \"%s\" wanted on %s is in use by device %s\n"),
430            dcr->VolumeName, dcr->dev->print_name(), dev->print_name());
431       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
432            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), dev->get_slot());
433       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->num_reserved());
434       volume_unused(dcr);
435       return false;
436    }
437    return unload_dev(dcr, dev);
438 }
439
440 /*
441  * Unconditionally unload a specified drive
442  */
443 bool unload_dev(DCR *dcr, DEVICE *dev)
444 {
445    JCR *jcr = dcr->jcr;
446    bool ok = true;
447    uint32_t timeout = dcr->device->max_changer_wait;
448    AUTOCHANGER *changer = dcr->dev->device->changer_res;
449    DEVICE *save_dev;
450    int save_slot;
451
452    if (!changer) {
453       return false;
454    }
455
456    save_dev = dcr->dev;               /* save dcr device */
457    dcr->dev = dev;                    /* temporarily point dcr at other device */
458
459    if (dev->get_slot() <= 0 && get_autochanger_loaded_slot(dcr) <= 0) {
460       dcr->dev = save_dev;
461       return false;
462    }
463    save_slot = dcr->VolCatInfo.Slot;
464    dcr->VolCatInfo.Slot = dev->get_slot();
465
466 //   dev->dlock();
467
468    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
469    POOL_MEM results(PM_MESSAGE);
470    lock_changer(dcr);
471    Jmsg(jcr, M_INFO, 0,
472         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
473         dev->get_slot(), dev->drive_index);
474
475    Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
476         dev->get_slot(), dev->drive_index);
477
478    changer_cmd = edit_device_codes(dcr, changer_cmd, 
479                 dcr->device->changer_command, "unload");
480    dev->close();
481    Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), 
482       dev->num_reserved());
483    Dmsg1(100, "Run program=%s\n", changer_cmd);
484    int stat = run_program_full_output(changer_cmd, timeout, results.addr());
485    dcr->VolCatInfo.Slot = save_slot;
486    dcr->dev = save_dev;
487    if (stat != 0) {
488       berrno be;
489       be.set_errno(stat);
490       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
491               dev->get_slot(), dev->drive_index, be.bstrerror());
492
493       Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
494               dev->get_slot(), dev->drive_index, be.bstrerror());
495       ok = false;
496       dev->clear_slot();          /* unknown */
497    } else {
498       Dmsg2(100, "Slot %d unloaded %s\n", dev->get_slot(), dev->print_name());
499       dev->set_slot(0);           /* nothing loaded */
500    }
501    if (ok) {
502       dev->clear_unload();
503    }
504    unlock_changer(dcr);
505
506 //   dev->dunlock();
507
508    free_volume(dev);               /* Free any volume associated with this drive */
509    free_pool_memory(changer_cmd);
510    return ok;
511 }
512
513
514
515 /*
516  * List the Volumes that are in the autoloader possibly
517  *   with their barcodes.
518  *   We assume that it is always the Console that is calling us.
519  */
520 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
521 {
522    DEVICE *dev = dcr->dev;
523    uint32_t timeout = dcr->device->max_changer_wait;
524    POOLMEM *changer;
525    BPIPE *bpipe;
526    int len = sizeof_pool_memory(dir->msg) - 1;
527    bool ok = false;
528    int stat;
529
530    if (!dev->is_autochanger() || !dcr->device->changer_name ||
531        !dcr->device->changer_command) {
532       if (strcmp(cmd, "drives") == 0) {
533          dir->fsend("drives=1\n");
534       }
535       dir->fsend(_("3993 Device %s not an autochanger device.\n"),
536          dev->print_name());
537       return false;
538    }
539
540    if (strcmp(cmd, "drives") == 0) {
541       AUTOCHANGER *changer_res = dcr->device->changer_res;
542       int drives = 1;
543       if (changer_res) {
544          drives = changer_res->device->size();
545       }
546       dir->fsend("drives=%d\n", drives);
547       Dmsg1(100, "drives=%d\n", drives);
548       return true;
549    }
550
551    changer = get_pool_memory(PM_FNAME);
552    lock_changer(dcr);
553    /* Now issue the command */
554    changer = edit_device_codes(dcr, changer, 
555                  dcr->device->changer_command, cmd);
556    dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd);
557    bpipe = open_bpipe(changer, timeout, "r");
558    if (!bpipe) {
559       dir->fsend(_("3996 Open bpipe failed.\n"));
560       goto bail_out;
561    }
562    if (bstrcmp(cmd, "list") || bstrcmp(cmd, "listall")) {
563       /* Get output from changer */
564       while (fgets(dir->msg, len, bpipe->rfd)) {
565          dir->msglen = strlen(dir->msg);
566          Dmsg1(100, "<stored: %s\n", dir->msg);
567          bnet_send(dir);
568       }
569    } else if (strcmp(cmd, "slots") == 0 ) {
570       char buf[100], *p;
571       /* For slots command, read a single line */
572       buf[0] = 0;
573       fgets(buf, sizeof(buf)-1, bpipe->rfd);
574       buf[sizeof(buf)-1] = 0;
575       /* Strip any leading space in front of # of slots */
576       for (p=buf; B_ISSPACE(*p); p++)
577         { }
578       dir->fsend("slots=%s", p);
579       Dmsg1(100, "<stored: %s", dir->msg);
580    } 
581                  
582    stat = close_bpipe(bpipe);
583    if (stat != 0) {
584       berrno be;
585       be.set_errno(stat);
586       dir->fsend(_("Autochanger error: ERR=%s\n"), be.bstrerror());
587    }
588    bnet_sig(dir, BNET_EOD);
589    ok = true;
590
591 bail_out:
592    unlock_changer(dcr);
593    free_pool_memory(changer);
594    return true;
595 }
596
597
598 /*
599  * Edit codes into ChangerCommand
600  *  %% = %
601  *  %a = archive device name
602  *  %c = changer device name
603  *  %d = changer drive index
604  *  %f = Client's name
605  *  %j = Job name
606  *  %o = command
607  *  %s = Slot base 0
608  *  %S = Slot base 1
609  *  %v = Volume name
610  *
611  *
612  *  omsg = edited output message
613  *  imsg = input string containing edit codes (%x)
614  *  cmd = command string (load, unload, ...)
615  *
616  */
617 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
618 {
619    const char *p;
620    const char *str;
621    char add[20];
622
623    *omsg = 0;
624    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
625    for (p=imsg; *p; p++) {
626       if (*p == '%') {
627          switch (*++p) {
628          case '%':
629             str = "%";
630             break;
631          case 'a':
632             str = dcr->dev->archive_name();
633             break;
634          case 'c':
635             str = NPRT(dcr->device->changer_name);
636             break;
637          case 'd':
638             sprintf(add, "%d", dcr->dev->drive_index);
639             str = add;
640             break;
641          case 'o':
642             str = NPRT(cmd);
643             break;
644          case 's':
645             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
646             str = add;
647             break;
648          case 'S':
649             sprintf(add, "%d", dcr->VolCatInfo.Slot);
650             str = add;
651             break;
652          case 'j':                    /* Job name */
653             str = dcr->jcr->Job;
654             break;
655          case 'v':
656             if (dcr->VolCatInfo.VolCatName[0]) {
657                str = dcr->VolCatInfo.VolCatName;
658             } else if (dcr->VolumeName[0]) {
659                str = dcr->VolumeName;
660             } else if (dcr->dev->vol && dcr->dev->vol->vol_name) {
661                str = dcr->dev->vol->vol_name;
662             } else {
663                str = dcr->dev->VolHdr.VolumeName;
664             }
665             break;
666          case 'f':
667             str = NPRT(dcr->jcr->client_name);
668             break;
669
670          default:
671             add[0] = '%';
672             add[1] = *p;
673             add[2] = 0;
674             str = add;
675             break;
676          }
677       } else {
678          add[0] = *p;
679          add[1] = 0;
680          str = add;
681       }
682       Dmsg1(1900, "add_str %s\n", str);
683       pm_strcat(&omsg, (char *)str);
684       Dmsg1(1800, "omsg=%s\n", omsg);
685    }
686    Dmsg1(800, "omsg=%s\n", omsg);
687    return omsg;
688 }