]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/dvd.c
- Detect device mounted for DVD and suppress be sure to
[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 ammended 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 char *edit_device_codes_dev(DEVICE *dev, char *omsg, const char *imsg);
30 static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout);
31 static bool dvd_write_part(DEVICE *dev);
32 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name);
33
34 /* 
35  * Write the current volume/part filename to archive_name.
36  */
37 void make_mounted_dvd_filename(DEVICE *dev, POOL_MEM &archive_name) 
38 {
39    pm_strcpy(archive_name, dev->device->mount_point);
40    add_file_and_part_name(dev, archive_name);
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 }      
53
54 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name)
55 {
56    char partnumber[20];
57    if (archive_name.c_str()[strlen(archive_name.c_str())-1] != '/') {
58       pm_strcat(archive_name, "/");
59    }
60
61    pm_strcat(archive_name, dev->VolCatInfo.VolCatName);
62    /* if part != 0, append .# to the filename (where # is the part number) */
63    if (dev->part != 0) {
64       pm_strcat(archive_name, ".");
65       bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part);
66       pm_strcat(archive_name, partnumber);
67    }
68    Dmsg1(100, "Exit make_dvd_filename: arch=%s\n", archive_name.c_str());
69 }  
70
71 /* Mount the device.
72  * If timeout, wait until the mount command returns 0.
73  * If !timeout, try to mount the device only once.
74  */
75 bool mount_dev(DEVICE* dev, int timeout) 
76 {
77    Dmsg0(900, "Enter mount_dev\n");
78    if (dev->is_mounted()) {
79       return true;
80    } else if (dev->requires_mount()) {
81       return do_mount_dev(dev, 1, timeout);
82    }       
83    return true;
84 }
85
86 /* Unmount the device
87  * If timeout, wait until the unmount command returns 0.
88  * If !timeout, try to unmount the device only once.
89  */
90 bool unmount_dev(DEVICE *dev, int timeout) 
91 {
92    Dmsg0(900, "Enter unmount_dev\n");
93    if (dev->is_mounted()) {
94       return do_mount_dev(dev, 0, timeout);
95    }
96    return true;
97 }
98
99 /* (Un)mount the device */
100 static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout) 
101 {
102    POOL_MEM ocmd(PM_FNAME);
103    POOLMEM *results;
104    char *icmd;
105    int status, timeout;
106    
107    if (mount) {
108       if (dev->is_mounted()) {
109          return true;
110       }
111       icmd = dev->device->mount_command;
112    } else {
113       if (!dev->is_mounted()) {
114          return true;
115       }
116       icmd = dev->device->unmount_command;
117    }
118    
119    edit_device_codes_dev(dev, ocmd.c_str(), icmd);
120    
121    Dmsg2(200, "do_mount_dev: cmd=%s mounted=%d\n", ocmd.c_str(), !!dev->is_mounted());
122
123    if (dotimeout) {
124       /* Try at most 1 time to (un)mount the device. This should perhaps be configurable. */
125       timeout = 1;
126    } else {
127       timeout = 0;
128    }
129    results = get_pool_memory(PM_MESSAGE);
130    results[0] = 0;
131    /* If busy retry each second */
132    while ((status = run_program_full_output(ocmd.c_str(), 
133                        dev->max_open_wait/2, results)) != 0) {
134       if (fnmatch("*is already mounted on", results, 0) == 0) {
135          break;
136       }
137       if (timeout-- > 0) {
138          /* Sometimes the device cannot be mounted because it is already mounted.
139           * Try to unmount it, then remount it */
140          if (mount) {
141             Dmsg1(400, "Trying to unmount the device %s...\n", dev->print_name());
142             do_mount_dev(dev, 0, 0);
143          }
144          bmicrosleep(1, 0);
145          continue;
146       }
147       Dmsg2(40, "Device %s cannot be mounted. ERR=%s\n", dev->print_name(), results);
148       Mmsg(dev->errmsg, "Device %s cannot be mounted. ERR=%s\n", 
149            dev->print_name(), results);
150       free_pool_memory(results);
151       return false;
152    }
153    
154    dev->set_mounted(mount);              /* set/clear mounted flag */
155    free_pool_memory(results);
156    return true;
157 }
158
159 /* Update the free space on the device */
160 void update_free_space_dev(DEVICE* dev) 
161 {
162    POOL_MEM ocmd(PM_FNAME);
163    POOLMEM* results;
164    char* icmd;
165    int timeout;
166    long long int free;
167    char ed1[50];
168    
169    icmd = dev->device->free_space_command;
170    
171    if (!icmd) {
172       dev->free_space = 0;
173       dev->free_space_errno = 0;
174       dev->clear_media();
175       Dmsg2(29, "update_free_space_dev: free_space=%d, free_space_errno=%d (!icmd)\n", dev->free_space, dev->free_space_errno);
176       return;
177    }
178    
179    edit_device_codes_dev(dev, ocmd.c_str(), icmd);
180    
181    Dmsg1(29, "update_free_space_dev: cmd=%s\n", ocmd.c_str());
182
183    results = get_pool_memory(PM_MESSAGE);
184    results[0] = 0;
185    
186    /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
187    timeout = 3;
188    
189    while (1) {
190       if (run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results) == 0) {
191          Dmsg1(100, "Free space program run : %s\n", results);
192          free = str_to_int64(results);
193          if (free >= 0) {
194             dev->free_space = free;
195             dev->free_space_errno = 1;
196             dev->set_media();
197             Mmsg0(dev->errmsg, "");
198             break;
199          }
200       }
201       dev->free_space = 0;
202       dev->free_space_errno = -EPIPE;
203       Mmsg1(dev->errmsg, "Cannot run free space command (%s)\n", results);
204       
205       if (--timeout > 0) {
206          Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
207             "free_space_errno=%d ERR=%s\n", dev->dev_name, 
208                edit_uint64(dev->free_space, ed1), dev->free_space_errno, 
209                dev->errmsg);
210          bmicrosleep(1, 0);
211          continue;
212       }
213
214       dev->dev_errno = -dev->free_space_errno;
215       Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
216          "free_space_errno=%d ERR=%s\n",
217             dev->dev_name, edit_uint64(dev->free_space, ed1),
218             dev->free_space_errno, dev->errmsg);
219       break;
220    }
221    
222    free_pool_memory(results);
223    Dmsg3(29, "update_free_space_dev: free_space=%s, free_space_errno=%d have_media=%d\n", 
224       edit_uint64(dev->free_space, ed1), dev->free_space_errno, dev->have_media());
225    return;
226 }
227
228 static bool dvd_write_part(DEVICE *dev) 
229 {
230    Dmsg1(29, "dvd_write_part: device is %s\n", dev->dev_name);
231    
232    if (unmount_dev(dev, 1) < 0) {
233       Dmsg0(29, "dvd_write_part: unable to unmount the device\n");
234    }
235    
236    POOL_MEM ocmd(PM_FNAME);
237    POOLMEM *results;
238    char* icmd;
239    int status;
240    int timeout;
241    
242    results = get_pool_memory(PM_MESSAGE);
243    results[0] = 0;
244    icmd = dev->device->write_part_command;
245    
246    edit_device_codes_dev(dev, ocmd.c_str(), icmd);
247       
248    /* Wait at most the time a maximum size part is written in DVD 0.5x speed
249     * FIXME: Minimum speed should be in device configuration 
250     */
251    timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2));
252    
253    Dmsg2(29, "dvd_write_part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout);
254       
255    status = run_program_full_output(ocmd.c_str(), timeout, results);
256    if (status != 0) {
257       Mmsg1(dev->errmsg, "Error while writing current part to the DVD: %s", 
258             results);
259       Dmsg1(000, "%s", dev->errmsg);
260       dev->dev_errno = EIO;
261       free_pool_memory(results);
262       return false;
263    }
264
265    POOL_MEM archive_name(PM_FNAME);
266    /* Delete spool file */
267    make_spooled_dvd_filename(dev, archive_name);
268    unlink(archive_name.c_str());
269    free_pool_memory(results);
270    return true;
271 }
272
273 /* Open the next part file.
274  *  - Close the fd
275  *  - Increment part number 
276  *  - Reopen the device
277  */
278 int open_next_part(DCR *dcr)
279 {
280    DEVICE *dev = dcr->dev;
281       
282    Dmsg5(29, "Enter: open_next_part part=%d npart=%d dev=%s vol=%s mode=%d\n", 
283       dev->part, dev->num_parts, dev->print_name(),
284          dev->VolCatInfo.VolCatName, dev->openmode);
285    /* When appending, do not open a new part if the current is empty */
286    if (dev->can_append() && (dev->part == dev->num_parts) && 
287        (dev->part_size == 0)) {
288       Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
289       return dev->fd;
290    }
291    
292    if (dev->fd >= 0) {
293       close(dev->fd);
294    }
295    
296    dev->fd = -1;
297    dev->clear_opened();
298    
299    /*
300     * If we have a part open for write, then write it to
301     *  DVD before opening the next part.
302     */
303    if (dev->is_dvd() && (dev->part == dev->num_parts) && dev->can_append()) {
304       if (!dvd_write_part(dev)) {
305          return -1;
306       }
307    }
308      
309    dev->part_start += dev->part_size;
310    dev->part++;
311    
312    Dmsg2(29, "part=%d num_parts=%d\n", dev->part, dev->num_parts);
313    if ((dev->num_parts < dev->part) && dev->can_append()) {
314       POOL_MEM archive_name(PM_FNAME);
315       struct stat buf;
316
317       /* 
318        * First check what is on DVD.  If out part is there, we
319        *   are in trouble, so bail out.
320        */
321       make_mounted_dvd_filename(dev, archive_name);   /* makes dvd name */
322       if (stat(archive_name.c_str(), &buf) == 0) {
323          /* bad news bail out */
324          Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"),
325             archive_name.c_str());
326          return -1;
327       }
328
329       dev->num_parts = dev->part;
330       make_spooled_dvd_filename(dev, archive_name);   /* makes spool name */
331       
332       /* Check if the next part exists in spool directory . */
333       if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
334          Dmsg1(29, "open_next_part %s is in the way, moving it away...\n", archive_name.c_str());
335          /* Then try to unlink it */
336          if (unlink(archive_name.c_str()) < 0) {
337             berrno be;
338             dev->dev_errno = errno;
339             Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), 
340                    archive_name.c_str(), be.strerror());
341             return -1;
342          }
343       }
344    }
345    
346    Dmsg2(50, "Call dev->open(vol=%s, mode=%d", dev->VolCatInfo.VolCatName, 
347          dev->openmode);
348    /* Open next part */
349    if (dev->open(dcr, dev->openmode) < 0) {
350       return -1;
351    } 
352    dev->set_labeled();          /* all next parts are "labeled" */
353    return dev->fd;
354 }
355
356 /* Open the first part file.
357  *  - Close the fd
358  *  - Reopen the device
359  */
360 int open_first_part(DCR *dcr, int mode)
361 {
362    DEVICE *dev = dcr->dev;
363    Dmsg3(29, "Enter: open_first_part dev=%s Vol=%s mode=%d\n", dev->dev_name, 
364          dev->VolCatInfo.VolCatName, dev->openmode);
365    if (dev->fd >= 0) {
366       close(dev->fd);
367    }
368    dev->fd = -1;
369    dev->clear_opened();
370    
371    dev->part_start = 0;
372    dev->part = 0;
373    
374    Dmsg2(50, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName, 
375          mode);
376    if (dev->open(dcr, mode) < 0) {
377       Dmsg0(50, "open dev() failed\n");
378       return -1;
379    }
380    Dmsg1(50, "Leave open_first_part state=%s\n", dev->is_open()?"open":"not open");
381    return dev->fd;
382 }
383
384
385 /* Protected version of lseek, which opens the right part if necessary */
386 off_t lseek_dev(DEVICE *dev, off_t offset, int whence)
387 {
388    int pos, openmode;
389    DCR *dcr;
390    
391    if (dev->num_parts == 0) { /* If there is only one part, simply call lseek. */
392       return lseek(dev->fd, offset, whence);
393    }
394       
395    dcr = (DCR *)dev->attached_dcrs->first();  /* any dcr will do */
396    switch(whence) {
397    case SEEK_SET:
398       Dmsg1(100, "lseek_dev SEEK_SET to %d\n", (int)offset);
399       if ((uint64_t)offset >= dev->part_start) {
400          if ((uint64_t)(offset - dev->part_start) < dev->part_size) {
401             /* We are staying in the current part, just seek */
402             if ((pos = lseek(dev->fd, (off_t)(offset-dev->part_start), SEEK_SET)) < 0) {
403                return pos;   
404             } else {
405                return pos + dev->part_start;
406             }
407          } else {
408             /* Load next part, and start again */
409             if (open_next_part(dcr) < 0) {
410                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
411                return -1;
412             }
413             return lseek_dev(dev, offset, SEEK_SET);
414          }
415       } else {
416          /* pos < dev->part_start :
417           * We need to access a previous part, 
418           * so just load the first one, and seek again
419           * until the right one is loaded */
420          if (open_first_part(dcr, dev->openmode) < 0) {
421             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
422             return -1;
423          }
424          return lseek_dev(dev, offset, SEEK_SET);
425       }
426       break;
427    case SEEK_CUR:
428       Dmsg1(100, "lseek_dev SEEK_CUR to %d\n", (int)offset);
429       if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
430          return pos;   
431       }
432       pos += dev->part_start;
433       if (offset == 0) {
434          return pos;
435       } else { /* Not used in Bacula, but should work */
436          return lseek_dev(dev, pos, SEEK_SET);
437       }
438       break;
439    case SEEK_END:
440       Dmsg1(100, "lseek_dev SEEK_END to %d\n", (int)offset);
441       if (offset > 0) { /* Not used by bacula */
442          Dmsg1(100, "lseek_dev SEEK_END called with an invalid offset %d\n", (int)offset);
443          errno = EINVAL;
444          return -1;
445       }
446       
447       if (dev->part == dev->num_parts) { /* The right part is already loaded */
448          if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
449             return pos;   
450          } else {
451             return pos + dev->part_start;
452          }
453       } else {
454          /* Load the first part, then load the next until we reach the last one.
455           * This is the only way to be sure we compute the right file address. */
456          /* Save previous openmode, and open all but last part read-only (useful for DVDs) */
457          openmode = dev->openmode;
458          
459          /* Works because num_parts > 0. */
460          if (open_first_part(dcr, OPEN_READ_ONLY) < 0) {
461             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
462             return -1;
463          }
464          while (dev->part < (dev->num_parts-1)) {
465             if (open_next_part(dcr) < 0) {
466                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
467                return -1;
468             }
469          }
470          dev->openmode = openmode;
471          if (open_next_part(dcr) < 0) {
472             Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
473             return -1;
474          }
475          return lseek_dev(dev, 0, SEEK_END);
476       }
477       break;
478    default:
479       errno = EINVAL;
480       return -1;
481    }
482 }
483
484 bool dvd_close_job(DCR *dcr)
485 {
486    DEVICE *dev = dcr->dev;
487    JCR *jcr = dcr->jcr;
488    bool ok = true;
489
490    /* If the device is a dvd and WritePartAfterJob
491     * is set to yes, open the next part, so, in case of a device
492     * that requires mount, it will be written to the device.
493     */
494    if (dev->is_dvd() && jcr->write_part_after_job && (dev->part_size > 0)) {
495       Dmsg0(100, "Writing last part because write_part_after_job is set.\n");
496       if (dev->part < dev->num_parts) {
497          Jmsg3(jcr, M_FATAL, 0, _("Error while writing, current part number is less than the total number of parts (%d/%d, device=%s)\n"),
498                dev->part, dev->num_parts, dev->print_name());
499          dev->dev_errno = EIO;
500          ok = false;
501       }
502       
503       if (ok && (open_next_part(dcr) < 0)) {
504          Jmsg2(jcr, M_FATAL, 0, _("Unable to open device next part %s: ERR=%s\n"),
505                dev->print_name(), strerror_dev(dev));
506          dev->dev_errno = EIO;
507          ok = false;
508       }
509       dev->VolCatInfo.VolCatParts = dev->num_parts;
510    }
511    return ok;
512 }
513
514
515 /*
516  * Edit codes into (Un)MountCommand, Write(First)PartCommand
517  *  %% = %
518  *  %a = archive device name
519  *  %m = mount point
520  *  %v = last part name
521  *
522  *  omsg = edited output message
523  *  imsg = input string containing edit codes (%x)
524  *
525  */
526 static char *edit_device_codes_dev(DEVICE* dev, char *omsg, const char *imsg)
527 {
528    const char *p;
529    const char *str;
530    char add[20];
531    
532    POOL_MEM archive_name(PM_FNAME);
533
534    *omsg = 0;
535    Dmsg1(800, "edit_device_codes: %s\n", imsg);
536    for (p=imsg; *p; p++) {
537       if (*p == '%') {
538          switch (*++p) {
539          case '%':
540             str = "%";
541             break;
542          case 'n':
543             bsnprintf(add, sizeof(add), "%d", dev->part);
544             str = add;
545             break;
546          case 'a':
547             str = dev->dev_name;
548             break;
549          case 'm':
550             str = dev->device->mount_point;
551             break;
552          case 'v':
553             make_spooled_dvd_filename(dev, archive_name);
554             str = archive_name.c_str();
555             break;
556          default:
557             add[0] = '%';
558             add[1] = *p;
559             add[2] = 0;
560             str = add;
561             break;
562          }
563       } else {
564          add[0] = *p;
565          add[1] = 0;
566          str = add;
567       }
568       Dmsg1(1900, "add_str %s\n", str);
569       pm_strcat(&omsg, (char *)str);
570       Dmsg1(1800, "omsg=%s\n", omsg);
571    }
572    return omsg;
573 }