]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/dvd.c
Fixes concerning DVD-writing.
[bacula/bacula] / bacula / src / stored / dvd.c
1 /*
2  *
3  *   dvd.c  -- Routines specific to DVD devices (and
4  *             possibly other removable hard media). 
5  *
6  *    Nicolas Boichat, MMV
7  *
8  *   Version $Id$
9  */
10 /*
11    Copyright (C) 2005 Kern Sibbald
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License
15    version 2 as amended with additional clauses defined in the
16    file LICENSE in the main source directory.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
21    the file LICENSE for additional details.
22
23  */
24
25 #include "bacula.h"
26 #include "stored.h"
27
28 /* Forward referenced functions */
29 static void edit_device_codes_dev(DEVICE *dev, POOL_MEM &omsg, const char *imsg);
30 static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout);
31 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name);
32
33 /* 
34  * Write the current volume/part filename to archive_name.
35  */
36 void make_mounted_dvd_filename(DEVICE *dev, POOL_MEM &archive_name) 
37 {
38    pm_strcpy(archive_name, dev->device->mount_point);
39    add_file_and_part_name(dev, archive_name);
40    dev->set_part_spooled(false);
41 }
42
43 void make_spooled_dvd_filename(DEVICE *dev, POOL_MEM &archive_name)
44 {
45    /* Use the working directory if spool directory is not defined */
46    if (dev->device->spool_directory) {
47       pm_strcpy(archive_name, dev->device->spool_directory);
48    } else {
49       pm_strcpy(archive_name, working_directory);
50    }
51    add_file_and_part_name(dev, archive_name);
52    dev->set_part_spooled(true);
53 }      
54
55 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name)
56 {
57    char partnumber[20];
58    if (archive_name.c_str()[strlen(archive_name.c_str())-1] != '/') {
59       pm_strcat(archive_name, "/");
60    }
61
62    pm_strcat(archive_name, dev->VolCatInfo.VolCatName);
63    /* if part > 1, append .# to the filename (where # is the part number) */
64    if (dev->part > 0) {
65       pm_strcat(archive_name, ".");
66       bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part);
67       pm_strcat(archive_name, partnumber);
68    }
69    Dmsg1(100, "Exit make_dvd_filename: arch=%s\n", archive_name.c_str());
70 }  
71
72 /* Mount the device.
73  * If timeout, wait until the mount command returns 0.
74  * If !timeout, try to mount the device only once.
75  */
76 bool mount_dev(DEVICE* dev, int timeout) 
77 {
78    Dmsg0(90, "Enter mount_dev\n");
79    if (dev->is_mounted()) {
80       return true;
81    } else if (dev->requires_mount()) {
82       return do_mount_dev(dev, 1, timeout);
83    }       
84    return true;
85 }
86
87 /* Unmount the device
88  * If timeout, wait until the unmount command returns 0.
89  * If !timeout, try to unmount the device only once.
90  */
91 bool unmount_dev(DEVICE *dev, int timeout) 
92 {
93    Dmsg0(90, "Enter unmount_dev\n");
94    if (dev->is_mounted()) {
95       return do_mount_dev(dev, 0, timeout);
96    }
97    return true;
98 }
99
100 /* (Un)mount the device */
101 static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout) 
102 {
103    POOL_MEM ocmd(PM_FNAME);
104    POOLMEM *results;
105    char *icmd;
106    int status, timeout;
107    
108    sm_check(__FILE__, __LINE__, false);
109    if (mount) {
110       if (dev->is_mounted()) {
111          Dmsg0(200, "======= DVD mount=1\n");
112          return true;
113       }
114       icmd = dev->device->mount_command;
115    } else {
116       if (!dev->is_mounted()) {
117          Dmsg0(200, "======= DVD mount=0\n");
118          return true;
119       }
120       icmd = dev->device->unmount_command;
121    }
122    
123    edit_device_codes_dev(dev, ocmd, icmd);
124    
125    Dmsg2(200, "do_mount_dev: cmd=%s mounted=%d\n", ocmd.c_str(), !!dev->is_mounted());
126
127    if (dotimeout) {
128       /* Try at most 1 time to (un)mount the device. This should perhaps be configurable. */
129       timeout = 1;
130    } else {
131       timeout = 0;
132    }
133    results = get_memory(2000);
134    results[0] = 0;
135    /* If busy retry each second */
136    while ((status = run_program_full_output(ocmd.c_str(), 
137                        dev->max_open_wait/2, results)) != 0) {
138       if (fnmatch("*is already mounted on", results, 0) == 0) {
139          break;
140       }
141       if (timeout-- > 0) {
142          /* Sometimes the device cannot be mounted because it is already mounted.
143           * Try to unmount it, then remount it */
144          if (mount) {
145             Dmsg1(400, "Trying to unmount the device %s...\n", dev->print_name());
146             do_mount_dev(dev, 0, 0);
147          }
148          bmicrosleep(1, 0);
149          continue;
150       }
151       Dmsg2(40, "Device %s cannot be mounted. ERR=%s\n", dev->print_name(), results);
152       Mmsg(dev->errmsg, "Device %s cannot be mounted. ERR=%s\n", 
153            dev->print_name(), results);
154       /*
155        * Now, just to be sure it is not mounted, try to read the
156        *  filesystem.
157        */
158       DIR* dp;
159       struct dirent *entry, *result;
160       int name_max;
161       int count = 0;
162       
163       name_max = pathconf(".", _PC_NAME_MAX);
164       if (name_max < 1024) {
165          name_max = 1024;
166       }
167          
168       if (!(dp = opendir(dev->device->mount_point))) {
169          berrno be;
170          dev->dev_errno = errno;
171          Dmsg3(29, "open_mounted_dev: failed to open dir %s (dev=%s), ERR=%s\n", 
172                dev->device->mount_point, dev->print_name(), be.strerror());
173          goto get_out;
174       }
175       
176       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
177       while (1) {
178          if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
179             dev->dev_errno = ENOENT;
180             Dmsg2(29, "open_mounted_dev: failed to find suitable file in dir %s (dev=%s)\n", 
181                   dev->device->mount_point, dev->print_name());
182             break;
183          }
184          count++;
185       }
186       free(entry);
187       closedir(dp);
188       if (count > 2) {
189          mount = 1;                      /* If we got more than . and .. */
190          break;                          /*   there must be something mounted */
191       }
192 get_out:
193       dev->set_mounted(false);
194       sm_check(__FILE__, __LINE__, false);
195       free_pool_memory(results);
196       Dmsg0(200, "============ DVD mount=0\n");
197       return false;
198    }
199    
200    dev->set_mounted(mount);              /* set/clear mounted flag */
201    free_pool_memory(results);
202    /* Do not check free space when unmounting (otherwise it will mount it again) */
203    if (mount) {
204       update_free_space_dev(dev);
205    }
206    Dmsg1(200, "============ DVD mount=%d\n", mount);
207    return true;
208 }
209
210 /* Update the free space on the device */
211 void update_free_space_dev(DEVICE* dev) 
212 {
213    POOL_MEM ocmd(PM_FNAME);
214    POOLMEM* results;
215    char* icmd;
216    int timeout;
217    long long int free;
218    char ed1[50];
219    
220    /* The device must be mounted in order to dvd-freespace to work */
221    mount_dev(dev, 1);
222    
223    sm_check(__FILE__, __LINE__, false);
224    icmd = dev->device->free_space_command;
225    
226    if (!icmd) {
227       dev->free_space = 0;
228       dev->free_space_errno = 0;
229       dev->clear_media();
230       Dmsg2(29, "update_free_space_dev: free_space=%d, free_space_errno=%d (!icmd)\n", dev->free_space, dev->free_space_errno);
231       return;
232    }
233    
234    edit_device_codes_dev(dev, ocmd, icmd);
235    
236    Dmsg1(29, "update_free_space_dev: cmd=%s\n", ocmd.c_str());
237
238    results = get_pool_memory(PM_MESSAGE);
239    
240    /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
241    timeout = 3;
242    
243    while (1) {
244       if (run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results) == 0) {
245          Dmsg1(100, "Free space program run : %s\n", results);
246          free = str_to_int64(results);
247          if (free >= 0) {
248             dev->free_space = free;
249             dev->free_space_errno = 1;
250             dev->set_media();
251             Mmsg0(dev->errmsg, "");
252             break;
253          }
254       }
255       dev->free_space = 0;
256       dev->free_space_errno = -EPIPE;
257       Mmsg1(dev->errmsg, "Cannot run free space command (%s)\n", results);
258       
259       if (--timeout > 0) {
260          Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
261             "free_space_errno=%d ERR=%s\n", dev->print_name(), 
262                edit_uint64(dev->free_space, ed1), dev->free_space_errno, 
263                dev->errmsg);
264          bmicrosleep(1, 0);
265          continue;
266       }
267
268       dev->dev_errno = -dev->free_space_errno;
269       Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
270          "free_space_errno=%d ERR=%s\n",
271             dev->print_name(), edit_uint64(dev->free_space, ed1),
272             dev->free_space_errno, dev->errmsg);
273       break;
274    }
275    
276    free_pool_memory(results);
277    Dmsg3(29, "update_free_space_dev: free_space=%s, free_space_errno=%d have_media=%d\n", 
278       edit_uint64(dev->free_space, ed1), dev->free_space_errno, dev->have_media());
279    sm_check(__FILE__, __LINE__, false);
280    return;
281 }
282
283 /*
284  * Write a part (Vol, Vol.1, ...) from the spool to the DVD   
285  * This MUST only be called from open_next_part. Otherwise the part number
286  * is not updated.
287  */
288 static bool dvd_write_part(DCR *dcr) 
289 {
290    DEVICE *dev = dcr->dev;
291    POOL_MEM ocmd(PM_FNAME);
292    char* icmd;
293    int status;
294    int timeout;
295    char ed1[50];
296    
297    sm_check(__FILE__, __LINE__, false);
298    Dmsg3(29, "dvd_write_part: device is %s, part is %d, is_mounted=%d\n", dev->print_name(), dev->part, dev->is_mounted());
299    icmd = dev->device->write_part_command;
300    
301    edit_device_codes_dev(dev, ocmd, icmd);
302       
303    /*
304     * Wait at most the time a maximum size part is written in DVD 0.5x speed
305     * FIXME: Minimum speed should be in device configuration 
306     */
307    timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2));
308    
309    Dmsg2(29, "dvd_write_part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout);
310       
311 {
312    POOL_MEM results(PM_MESSAGE);
313    sm_check(__FILE__, __LINE__, false);
314    status = run_program_full_output(ocmd.c_str(), timeout, results.c_str());
315    sm_check(__FILE__, __LINE__, false);
316    if (status != 0) {
317       Mmsg1(dev->errmsg, "Error while writing current part to the DVD: %s", 
318             results.c_str());
319       Dmsg1(000, "%s", dev->errmsg);
320       dev->dev_errno = EIO;
321       return false;
322    }
323    sm_check(__FILE__, __LINE__, false);
324 }
325
326 {
327    POOL_MEM archive_name(PM_FNAME);
328    /* Delete spool file */
329    make_spooled_dvd_filename(dev, archive_name);
330    unlink(archive_name.c_str());
331    Dmsg1(29, "unlink(%s)\n", archive_name.c_str());
332    sm_check(__FILE__, __LINE__, false);
333 }
334    
335    /* growisofs umount the device, so remount it (it will update the free space) */
336    dev->clear_mounted();
337    mount_dev(dev, 1);
338    Jmsg(dcr->jcr, M_INFO, 0, _("Remaining free space %s on %s\n"), 
339       edit_uint64_with_commas(dev->free_space, ed1), dev->print_name());
340    sm_check(__FILE__, __LINE__, false);
341    return true;
342 }
343
344 /* Open the next part file.
345  *  - Close the fd
346  *  - Increment part number 
347  *  - Reopen the device
348  */
349 int open_next_part(DCR *dcr)
350 {
351    DEVICE *dev = dcr->dev;
352
353    Dmsg5(29, "Enter: ==== open_next_part part=%d npart=%d dev=%s vol=%s mode=%d\n", 
354       dev->part, dev->num_parts, dev->print_name(),
355          dev->VolCatInfo.VolCatName, dev->openmode);
356    if (!dev->is_dvd()) {
357       Dmsg1(000, "Device %s is not dvd!!!!\n", dev->print_name()); 
358       return -1;
359    }
360       
361    /* When appending, do not open a new part if the current is empty */
362    if (dev->can_append() && (dev->part >= dev->num_parts) && 
363        (dev->part_size == 0)) {
364       Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
365       return dev->fd;
366    }
367    
368    if (dev->fd >= 0) {
369       close(dev->fd);
370    }
371    
372    dev->fd = -1;
373    dev->clear_opened();
374    
375    /*
376     * If we have a part open for write, then write it to
377     *  DVD before opening the next part.
378     */
379    if (dev->is_dvd() && (dev->part >= dev->num_parts) && dev->can_append()) {
380       if (!dvd_write_part(dcr)) {
381          return -1;
382       }
383    }
384      
385    if (dev->part > dev->num_parts) {
386       Dmsg2(000, "In open_next_part: part=%d nump=%d\n", dev->part, dev->num_parts);
387       ASSERT(dev->part <= dev->num_parts);
388    }
389    dev->part_start += dev->part_size;
390    dev->part++;
391    
392    Dmsg2(29, "part=%d num_parts=%d\n", dev->part, dev->num_parts);
393    /* I think this dev->can_append() should not be there */
394    if ((dev->num_parts < dev->part) && dev->can_append()) {
395       POOL_MEM archive_name(PM_FNAME);
396       struct stat buf;
397       /* 
398        * First check what is on DVD.  If our part is there, we
399        *   are in trouble, so bail out.
400        * NB: This is however not a problem if we are writing the first part.
401        * It simply means that we are overriding an existing volume...
402        */
403       if (dev->num_parts > 0) {
404          make_mounted_dvd_filename(dev, archive_name);   /* makes dvd name */
405          if (stat(archive_name.c_str(), &buf) == 0) {
406             /* bad news bail out */
407             Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"),
408                archive_name.c_str());
409             return -1;
410          }
411       }
412
413       Dmsg2(100, "Set npart=%d to part=%d\n", dev->num_parts, dev->part);
414       dev->num_parts = dev->part;
415       dev->VolCatInfo.VolCatParts = dev->part;
416       make_spooled_dvd_filename(dev, archive_name);   /* makes spool name */
417       
418       /* Check if the next part exists in spool directory . */
419       if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
420          Dmsg1(29, "open_next_part %s is in the way, moving it away...\n", archive_name.c_str());
421          /* Then try to unlink it */
422          if (unlink(archive_name.c_str()) < 0) {
423             berrno be;
424             dev->dev_errno = errno;
425             Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), 
426                    archive_name.c_str(), be.strerror());
427             return -1;
428          }
429       }
430    }
431    if (dev->num_parts < dev->part) {
432       Dmsg2(100, "Set npart=%d to part=%d\n", dev->num_parts, dev->part);
433       dev->num_parts = dev->part;
434       dev->VolCatInfo.VolCatParts = dev->part;
435    }
436    Dmsg2(50, "Call dev->open(vol=%s, mode=%d\n", dev->VolCatInfo.VolCatName, 
437          dev->openmode);
438    /* Open next part */
439    if (dev->open(dcr, dev->openmode) < 0) {
440       return -1;
441    } 
442    dev->set_labeled();          /* all next parts are "labeled" */
443    return dev->fd;
444 }
445
446 /* Open the first part file.
447  *  - Close the fd
448  *  - Reopen the device
449  */
450 int open_first_part(DCR *dcr, int mode)
451 {
452    DEVICE *dev = dcr->dev;
453
454    Dmsg3(29, "Enter: ==== open_first_part dev=%s Vol=%s mode=%d\n", dev->print_name(), 
455          dev->VolCatInfo.VolCatName, dev->openmode);
456
457    if (dev->fd >= 0) {
458       close(dev->fd);
459    }
460    dev->fd = -1;
461    dev->clear_opened();
462    
463    dev->part_start = 0;
464    dev->part = 0;
465    
466    Dmsg2(50, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName, 
467          mode);
468    if (dev->open(dcr, mode) < 0) {
469       Dmsg0(50, "open dev() failed\n");
470       return -1;
471    }
472    Dmsg1(50, "Leave open_first_part state=%s\n", dev->is_open()?"open":"not open");
473    return dev->fd;
474 }
475
476
477 /* Protected version of lseek, which opens the right part if necessary */
478 off_t lseek_dev(DEVICE *dev, off_t offset, int whence)
479 {
480    DCR *dcr;
481    off_t pos;
482    
483    Dmsg3(100, "Enter lseek_dev fd=%d part=%d nparts=%d\n", dev->fd,
484       dev->part, dev->num_parts);
485    if (!dev->is_dvd()) { 
486       Dmsg0(100, "Using sys lseek\n");
487       return lseek(dev->fd, offset, whence);
488    }
489       
490    dcr = (DCR *)dev->attached_dcrs->first();  /* any dcr will do */
491    switch(whence) {
492    case SEEK_SET:
493       Dmsg1(100, "lseek_dev SEEK_SET to %d\n", (int)offset);
494       if ((uint64_t)offset >= dev->part_start) {
495          offset -= dev->part_start; /* adjust for start of this part */
496          if (offset == 0 || (uint64_t)offset < dev->part_size) {
497             /* We are staying in the current part, just seek */
498             if ((pos = lseek(dev->fd, offset, SEEK_SET)) < 0) {
499                return pos;
500             } else {
501                return pos + dev->part_start;
502             }
503          } else {
504             /* Load next part, and start again */
505             if (open_next_part(dcr) < 0) {
506                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
507                return -1;
508             }
509             return lseek_dev(dev, offset, SEEK_SET);
510          }
511       } else {
512          /*
513           * pos < dev->part_start :
514           * We need to access a previous part, 
515           * so just load the first one, and seek again
516           * until the right one is loaded
517           */
518          if (open_first_part(dcr, dev->openmode) < 0) {
519             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
520             return -1;
521          }
522          return lseek_dev(dev, offset, SEEK_SET);
523       }
524       break;
525    case SEEK_CUR:
526       Dmsg1(100, "lseek_dev SEEK_CUR to %d\n", (int)offset);
527       if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
528          return pos;   
529       }
530       pos += dev->part_start;
531       if (offset == 0) {
532          Dmsg1(100, "lseek_dev SEEK_CUR returns %d\n", pos);
533          return pos;
534       } else { /* Not used in Bacula, but should work */
535          return lseek_dev(dev, pos, SEEK_SET);
536       }
537       break;
538    case SEEK_END:
539       Dmsg1(100, "lseek_dev SEEK_END to %d\n", (int)offset);
540       /*
541        * Bacula does not use offsets for SEEK_END
542        *  Also, Bacula uses seek_end only when it wants to
543        *  append to the volume, so for a dvd that means
544        *  that the volume must be spooled since the DVD
545        *  itself is read-only (as currently implemented).
546        */
547       if (offset > 0) { /* Not used by bacula */
548          Dmsg1(100, "lseek_dev SEEK_END called with an invalid offset %d\n", (int)offset);
549          errno = EINVAL;
550          return -1;
551       }
552       /* If we are already on a spooled part and have the
553        *  right part number, simply seek
554        */
555       if (dev->is_part_spooled() && dev->part == dev->num_parts) {
556          if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
557             return pos;   
558          } else {
559             Dmsg1(100, "lseek_dev SEEK_END returns %d\n", pos + dev->part_start);
560             return pos + dev->part_start;
561          }
562       } else {
563          /*
564           * Load the first part, then load the next until we reach the last one.
565           * This is the only way to be sure we compute the right file address.
566           *
567           * Save previous openmode, and open all but last part read-only 
568           * (useful for DVDs) 
569           */
570          int modesave = dev->openmode;
571          /* Works because num_parts > 0. */
572          if (open_first_part(dcr, OPEN_READ_ONLY) < 0) {
573             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
574             return -1;
575          }
576          if (dev->num_parts > 0) {
577             while (dev->part < (dev->num_parts-1)) {
578                if (open_next_part(dcr) < 0) {
579                   Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
580                   return -1;
581                }
582             }
583             dev->openmode = modesave;
584             if (open_next_part(dcr) < 0) {
585                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
586                return -1;
587             }
588          }
589          return lseek_dev(dev, 0, SEEK_END);
590       }
591       break;
592    default:
593       errno = EINVAL;
594       return -1;
595    }
596 }
597
598 bool dvd_close_job(DCR *dcr)
599 {
600    DEVICE *dev = dcr->dev;
601    JCR *jcr = dcr->jcr;
602    bool ok = true;
603
604    /* If the device is a dvd and WritePartAfterJob
605     * is set to yes, open the next part, so, in case of a device
606     * that requires mount, it will be written to the device.
607     */
608    if (dev->is_dvd() && jcr->write_part_after_job && (dev->part_size > 0)) {
609       Dmsg1(100, "Writing last part=%d write_partafter_job is set.\n",
610          dev->part);
611       if (dev->part < dev->num_parts) {
612          Jmsg3(jcr, M_FATAL, 0, _("Error while writing, current part number is less than the total number of parts (%d/%d, device=%s)\n"),
613                dev->part, dev->num_parts, dev->print_name());
614          dev->dev_errno = EIO;
615          ok = false;
616       }
617       
618       /* This should be !dvd_write_part(dcr)
619          NB: No! If you call dvd_write_part, the part number is not updated.
620          You must open the next part, it will automatically write the part and
621          update the part number. */
622       if (ok && (open_next_part(dcr) < 0)) {
623 //      if (ok && !dvd_write_part(dcr)) {
624          Jmsg2(jcr, M_FATAL, 0, _("Unable to write part %s: ERR=%s\n"),
625                dev->print_name(), strerror_dev(dev));
626          dev->dev_errno = EIO;
627          ok = false;
628       }
629    }
630    Dmsg1(200, "Set VolCatParts=%d\n", dev->num_parts);
631    dev->VolCatInfo.VolCatParts = dev->num_parts;
632    return ok;
633 }
634
635
636 /*
637  * Edit codes into (Un)MountCommand, Write(First)PartCommand
638  *  %% = %
639  *  %a = archive device name
640  *  %e = erase (set if cannot mount and first part)
641  *  %m = mount point
642  *  %v = last part name
643  *
644  *  omsg = edited output message
645  *  imsg = input string containing edit codes (%x)
646  *
647  */
648 static void edit_device_codes_dev(DEVICE* dev, POOL_MEM &omsg, const char *imsg)
649 {
650    const char *p;
651    const char *str;
652    char add[20];
653    
654    POOL_MEM archive_name(PM_FNAME);
655
656    omsg.c_str()[0] = 0;
657    Dmsg1(800, "edit_device_codes: %s\n", imsg);
658    for (p=imsg; *p; p++) {
659       if (*p == '%') {
660          switch (*++p) {
661          case '%':
662             str = "%";
663             break;
664          case 'a':
665             str = dev->dev_name;
666             break;
667          case 'e':
668             if (dev->part == 0 && !dev->is_mounted()) {
669                str = "1";
670             } else {
671                str = "0";
672             }
673             break;
674          case 'n':
675             bsnprintf(add, sizeof(add), "%d", dev->part);
676             str = add;
677             break;
678          case 'm':
679             str = dev->device->mount_point;
680             break;
681          case 'v':
682             make_spooled_dvd_filename(dev, archive_name);
683             str = archive_name.c_str();
684             break;
685          default:
686             add[0] = '%';
687             add[1] = *p;
688             add[2] = 0;
689             str = add;
690             break;
691          }
692       } else {
693          add[0] = *p;
694          add[1] = 0;
695          str = add;
696       }
697       Dmsg1(1900, "add_str %s\n", str);
698       pm_strcat(omsg, (char *)str);
699       Dmsg1(1800, "omsg=%s\n", omsg.c_str());
700    }
701 }