]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/dvd.c
01b6d0597af0cb7822dc382650bef64c8c73484f
[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    Dmsg6(29, "Enter: ==== open_next_part part=%d npart=%d dev=%s vol=%s mode=%d file_addr=%d\n", 
354       dev->part, dev->num_parts, dev->print_name(),
355          dev->VolCatInfo.VolCatName, dev->openmode, dev->file_addr);
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       Dmsg2(100, "lseek_dev SEEK_SET to %d (part_start=%d)\n", (int)offset, (int)dev->part_start);
494       if ((uint64_t)offset >= dev->part_start) {
495          if (((uint64_t)offset == dev->part_start) || ((uint64_t)offset < (dev->part_start+dev->part_size))) {
496             /* We are staying in the current part, just seek */
497             if ((pos = lseek(dev->fd, offset-dev->part_start, SEEK_SET)) < 0) {
498                return pos;
499             } else {
500                return pos + dev->part_start;
501             }
502          } else {
503             /* Load next part, and start again */
504             if (open_next_part(dcr) < 0) {
505                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
506                return -1;
507             }
508             return lseek_dev(dev, offset, SEEK_SET);
509          }
510       } else {
511          /*
512           * pos < dev->part_start :
513           * We need to access a previous part, 
514           * so just load the first one, and seek again
515           * until the right one is loaded
516           */
517          if (open_first_part(dcr, dev->openmode) < 0) {
518             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
519             return -1;
520          }
521          return lseek_dev(dev, offset, SEEK_SET);
522       }
523       break;
524    case SEEK_CUR:
525       Dmsg1(100, "lseek_dev SEEK_CUR to %d\n", (int)offset);
526       if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
527          return pos;   
528       }
529       pos += dev->part_start;
530       if (offset == 0) {
531          Dmsg1(100, "lseek_dev SEEK_CUR returns %d\n", pos);
532          return pos;
533       } else { /* Not used in Bacula, but should work */
534          return lseek_dev(dev, pos, SEEK_SET);
535       }
536       break;
537    case SEEK_END:
538       Dmsg1(100, "lseek_dev SEEK_END to %d\n", (int)offset);
539       /*
540        * Bacula does not use offsets for SEEK_END
541        *  Also, Bacula uses seek_end only when it wants to
542        *  append to the volume, so for a dvd that means
543        *  that the volume must be spooled since the DVD
544        *  itself is read-only (as currently implemented).
545        */
546       if (offset > 0) { /* Not used by bacula */
547          Dmsg1(100, "lseek_dev SEEK_END called with an invalid offset %d\n", (int)offset);
548          errno = EINVAL;
549          return -1;
550       }
551       /* If we are already on a spooled part and have the
552        *  right part number, simply seek
553        */
554       if (dev->is_part_spooled() && dev->part == dev->num_parts) {
555          if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
556             return pos;   
557          } else {
558             Dmsg1(100, "lseek_dev SEEK_END returns %d\n", pos + dev->part_start);
559             return pos + dev->part_start;
560          }
561       } else {
562          /*
563           * Load the first part, then load the next until we reach the last one.
564           * This is the only way to be sure we compute the right file address.
565           *
566           * Save previous openmode, and open all but last part read-only 
567           * (useful for DVDs) 
568           */
569          int modesave = dev->openmode;
570          /* Works because num_parts > 0. */
571          if (open_first_part(dcr, OPEN_READ_ONLY) < 0) {
572             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
573             return -1;
574          }
575          if (dev->num_parts > 0) {
576             while (dev->part < (dev->num_parts-1)) {
577                if (open_next_part(dcr) < 0) {
578                   Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
579                   return -1;
580                }
581             }
582             dev->openmode = modesave;
583             if (open_next_part(dcr) < 0) {
584                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
585                return -1;
586             }
587          }
588          return lseek_dev(dev, 0, SEEK_END);
589       }
590       break;
591    default:
592       errno = EINVAL;
593       return -1;
594    }
595 }
596
597 bool dvd_close_job(DCR *dcr)
598 {
599    DEVICE *dev = dcr->dev;
600    JCR *jcr = dcr->jcr;
601    bool ok = true;
602
603    /* If the device is a dvd and WritePartAfterJob
604     * is set to yes, open the next part, so, in case of a device
605     * that requires mount, it will be written to the device.
606     */
607    if (dev->is_dvd() && jcr->write_part_after_job && (dev->part_size > 0)) {
608       Dmsg1(100, "Writing last part=%d write_partafter_job is set.\n",
609          dev->part);
610       if (dev->part < dev->num_parts) {
611          Jmsg3(jcr, M_FATAL, 0, _("Error while writing, current part number is less than the total number of parts (%d/%d, device=%s)\n"),
612                dev->part, dev->num_parts, dev->print_name());
613          dev->dev_errno = EIO;
614          ok = false;
615       }
616       
617       /* This should be !dvd_write_part(dcr)
618          NB: No! If you call dvd_write_part, the part number is not updated.
619          You must open the next part, it will automatically write the part and
620          update the part number. */
621       if (ok && (open_next_part(dcr) < 0)) {
622 //      if (ok && !dvd_write_part(dcr)) {
623          Jmsg2(jcr, M_FATAL, 0, _("Unable to write part %s: ERR=%s\n"),
624                dev->print_name(), strerror_dev(dev));
625          dev->dev_errno = EIO;
626          ok = false;
627       }
628    }
629    Dmsg1(200, "Set VolCatParts=%d\n", dev->num_parts);
630    dev->VolCatInfo.VolCatParts = dev->num_parts;
631    return ok;
632 }
633
634
635 /*
636  * Edit codes into (Un)MountCommand, Write(First)PartCommand
637  *  %% = %
638  *  %a = archive device name
639  *  %e = erase (set if cannot mount and first part)
640  *  %m = mount point
641  *  %v = last part name
642  *
643  *  omsg = edited output message
644  *  imsg = input string containing edit codes (%x)
645  *
646  */
647 static void edit_device_codes_dev(DEVICE* dev, POOL_MEM &omsg, const char *imsg)
648 {
649    const char *p;
650    const char *str;
651    char add[20];
652    
653    POOL_MEM archive_name(PM_FNAME);
654
655    omsg.c_str()[0] = 0;
656    Dmsg1(800, "edit_device_codes: %s\n", imsg);
657    for (p=imsg; *p; p++) {
658       if (*p == '%') {
659          switch (*++p) {
660          case '%':
661             str = "%";
662             break;
663          case 'a':
664             str = dev->dev_name;
665             break;
666          case 'e':
667             if (dev->part == 0 && !dev->is_mounted()) {
668                str = "1";
669             } else {
670                str = "0";
671             }
672             break;
673          case 'n':
674             bsnprintf(add, sizeof(add), "%d", dev->part);
675             str = add;
676             break;
677          case 'm':
678             str = dev->device->mount_point;
679             break;
680          case 'v':
681             make_spooled_dvd_filename(dev, archive_name);
682             str = archive_name.c_str();
683             break;
684          default:
685             add[0] = '%';
686             add[1] = *p;
687             add[2] = 0;
688             str = add;
689             break;
690          }
691       } else {
692          add[0] = *p;
693          add[1] = 0;
694          str = add;
695       }
696       Dmsg1(1900, "add_str %s\n", str);
697       pm_strcat(omsg, (char *)str);
698       Dmsg1(1800, "omsg=%s\n", omsg.c_str());
699    }
700 }