ddae35e45130897c5b44aa2e8cad4934a9a7c76d
[debian/amanda] / device-src / dvdrw-device.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 2009 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: Sam Couter <sam@couter.id.au>
24  */
25
26 #include "amanda.h"
27 #include "vfs-device.h"
28
29 /*
30  * Type checking and casting macros
31  */
32 #define TYPE_DVDRW_DEVICE       (dvdrw_device_get_type())
33 #define DVDRW_DEVICE(obj)       G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DVDRW_DEVICE, DvdRwDevice)
34 #define DVDRW_DEVICE_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DVDRW_DEVICE, DvdRwDevice const)
35 #define DVDRW_DEVICE_CLASS(klass)       G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DVDRW_DEVICE, DvdRwDeviceClass)
36 #define IS_DVDRW_DEVICE(obj)    G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DVDRW_DEVICE)
37 #define DVDRW_DEVICE_GET_CLASS(obj)     G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DVDRW_DEVICE, DvdRwDeviceClass)
38
39 /* Forward declaration */
40 static GType dvdrw_device_get_type(void);
41
42 /*
43  * Main object structure
44  */
45 typedef struct _DvdRwDevice DvdRwDevice;
46 struct _DvdRwDevice {
47     VfsDevice __parent__;
48
49     gchar *dvdrw_device;
50     gchar *cache_dir;
51     gchar *cache_data;
52     gchar *mount_point;
53     gchar *mount_data;
54     gboolean mounted;
55     gboolean keep_cache;
56     gboolean unlabelled_when_unmountable;
57     gchar *growisofs_command;
58     gchar *mount_command;
59     gchar *umount_command;
60 };
61
62 /*
63  * Class definition
64  */
65 typedef struct _DvdRwDeviceClass DvdRwDeviceClass;
66 struct _DvdRwDeviceClass {
67     VfsDeviceClass __parent__;
68 };
69
70 /* Where the DVD-RW can be mounted */
71 static DevicePropertyBase device_property_dvdrw_mount_point;
72 #define PROPERTY_DVDRW_MOUNT_POINT (device_property_dvdrw_mount_point.ID)
73
74 /* Should the on-disk version be kept after the optical disc has been written? */
75 static DevicePropertyBase device_property_dvdrw_keep_cache;
76 #define PROPERTY_DVDRW_KEEP_CACHE (device_property_dvdrw_keep_cache.ID)
77
78 /* Should a mount failure (eg, a freshly formatted disc) when reading a label be treated like an unlabelled volume? */
79 static DevicePropertyBase device_property_dvdrw_unlabelled_when_unmountable;
80 #define PROPERTY_DVDRW_UNLABELLED_WHEN_UNMOUNTABLE (device_property_dvdrw_unlabelled_when_unmountable.ID)
81
82 /* Where to find the growisofs command */
83 static DevicePropertyBase device_property_dvdrw_growisofs_command;
84 #define PROPERTY_DVDRW_GROWISOFS_COMMAND (device_property_dvdrw_growisofs_command.ID)
85
86 /* Where to find the filesystem mount command */
87 static DevicePropertyBase device_property_dvdrw_mount_command;
88 #define PROPERTY_DVDRW_MOUNT_COMMAND (device_property_dvdrw_mount_command.ID)
89
90 /* Where to find the filesystem unmount command */
91 static DevicePropertyBase device_property_dvdrw_umount_command;
92 #define PROPERTY_DVDRW_UMOUNT_COMMAND (device_property_dvdrw_umount_command.ID)
93
94 /* GObject housekeeping */
95 void
96 dvdrw_device_register(void);
97
98 static Device*
99 dvdrw_device_factory(char *device_name, char *device_type, char *device_node);
100
101 static void
102 dvdrw_device_class_init (DvdRwDeviceClass *c);
103
104 static void
105 dvdrw_device_init (DvdRwDevice *self);
106
107 /* Properties */
108 static gboolean
109 dvdrw_device_set_mount_point_fn(Device *self,
110     DevicePropertyBase *base, GValue *val,
111     PropertySurety surety, PropertySource source);
112
113 static gboolean
114 dvdrw_device_set_keep_cache_fn(Device *self,
115     DevicePropertyBase *base, GValue *val,
116     PropertySurety surety, PropertySource source);
117
118 static gboolean
119 dvdrw_device_set_unlabelled_when_unmountable_fn(Device *self,
120     DevicePropertyBase *base, GValue *val,
121     PropertySurety surety, PropertySource source);
122
123 static gboolean
124 dvdrw_device_set_growisofs_command_fn(Device *self,
125     DevicePropertyBase *base, GValue *val,
126     PropertySurety surety, PropertySource source);
127
128 static gboolean
129 dvdrw_device_set_mount_command_fn(Device *self,
130     DevicePropertyBase *base, GValue *val,
131     PropertySurety surety, PropertySource source);
132
133 static gboolean
134 dvdrw_device_set_umount_command_fn(Device *self,
135     DevicePropertyBase *base, GValue *val,
136     PropertySurety surety, PropertySource source);
137
138 /* Methods */
139 static void
140 dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node);
141
142 static DeviceStatusFlags
143 dvdrw_device_read_label(Device *dself);
144
145 static gboolean
146 dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp);
147
148 static gboolean
149 dvdrw_device_finish(Device *dself);
150
151 static void
152 dvdrw_device_finalize(GObject *gself);
153
154 /* Helper functions */
155 static gboolean
156 check_access_mode(DvdRwDevice *self, DeviceAccessMode mode);
157
158 static gboolean
159 check_readable(DvdRwDevice *self);
160
161 static DeviceStatusFlags
162 mount_disc(DvdRwDevice *self, gboolean report_error);
163
164 static void
165 unmount_disc(DvdRwDevice *self);
166
167 static gboolean
168 burn_disc(DvdRwDevice *self);
169
170 static DeviceStatusFlags
171 execute_command(DvdRwDevice *self, gchar **argv, gint *status);
172
173 static GType
174 dvdrw_device_get_type (void)
175 {
176     static GType type = 0;
177
178     if G_UNLIKELY(type == 0) {
179         static const GTypeInfo info = {
180             sizeof (VfsDeviceClass),
181             (GBaseInitFunc) NULL,
182             (GBaseFinalizeFunc) NULL,
183             (GClassInitFunc) dvdrw_device_class_init,
184             (GClassFinalizeFunc) NULL,
185             NULL /* class_data */,
186             sizeof (VfsDevice),
187             0 /* n_preallocs */,
188             (GInstanceInitFunc) dvdrw_device_init,
189             NULL
190         };
191
192         type = g_type_register_static (TYPE_VFS_DEVICE, "DvdRwDevice",
193                                        &info, (GTypeFlags)0);
194     }
195
196     return type;
197 }
198
199 void
200 dvdrw_device_register(void)
201 {
202     const char *device_prefix_list[] = { "dvdrw", NULL };
203
204     device_property_fill_and_register(&device_property_dvdrw_mount_point,
205         G_TYPE_STRING, "dvdrw_mount_point",
206         "Directory to mount DVD-RW for reading");
207
208     device_property_fill_and_register(&device_property_dvdrw_keep_cache,
209         G_TYPE_BOOLEAN, "dvdrw_keep_cache",
210         "Keep on-disk cache after DVD-RW has been written");
211
212     device_property_fill_and_register(&device_property_dvdrw_unlabelled_when_unmountable,
213         G_TYPE_BOOLEAN, "dvdrw_unlabelled_when_unmountable",
214         "Treat unmountable volumes as unlabelled when reading label");
215
216     device_property_fill_and_register(&device_property_dvdrw_growisofs_command,
217         G_TYPE_BOOLEAN, "dvdrw_growisofs_command",
218         "The location of the growisofs command used to write the DVD-RW");
219
220     device_property_fill_and_register(&device_property_dvdrw_mount_command,
221         G_TYPE_BOOLEAN, "dvdrw_mount_command",
222         "The location of the mount command used to mount the DVD-RW filesystem for reading");
223
224     device_property_fill_and_register(&device_property_dvdrw_umount_command,
225         G_TYPE_BOOLEAN, "dvdrw_umount_command",
226         "The location of the umount command used to unmount the DVD-RW filesystem after reading");
227
228     register_device(dvdrw_device_factory, device_prefix_list);
229 }
230
231 static Device *
232 dvdrw_device_factory(char *device_name, char *device_type, char *device_node)
233 {
234     Device *device;
235
236     g_assert(0 == strncmp(device_type, "dvdrw", strlen("dvdrw")));
237
238     device = DEVICE(g_object_new(TYPE_DVDRW_DEVICE, NULL));
239     device_open_device(device, device_name, device_type, device_node);
240
241     return device;
242 }
243
244 static void
245 dvdrw_device_class_init (DvdRwDeviceClass *c)
246 {
247     DeviceClass *device_class = DEVICE_CLASS(c);
248     GObjectClass *g_object_class = G_OBJECT_CLASS(c);
249
250     device_class->open_device = dvdrw_device_open_device;
251     device_class->read_label = dvdrw_device_read_label;
252     device_class->start = dvdrw_device_start;
253     device_class->finish = dvdrw_device_finish;
254
255     g_object_class->finalize = dvdrw_device_finalize;
256
257     device_class_register_property(device_class, PROPERTY_DVDRW_MOUNT_POINT,
258         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
259         device_simple_property_get_fn,
260         dvdrw_device_set_mount_point_fn);
261
262     device_class_register_property(device_class, PROPERTY_DVDRW_KEEP_CACHE,
263         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
264         device_simple_property_get_fn,
265         dvdrw_device_set_keep_cache_fn);
266
267     device_class_register_property(device_class, PROPERTY_DVDRW_UNLABELLED_WHEN_UNMOUNTABLE,
268         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
269         device_simple_property_get_fn,
270         dvdrw_device_set_unlabelled_when_unmountable_fn);
271
272     device_class_register_property(device_class, PROPERTY_DVDRW_GROWISOFS_COMMAND,
273         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
274         device_simple_property_get_fn,
275         dvdrw_device_set_growisofs_command_fn);
276
277     device_class_register_property(device_class, PROPERTY_DVDRW_MOUNT_COMMAND,
278         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
279         device_simple_property_get_fn,
280         dvdrw_device_set_mount_command_fn);
281
282     device_class_register_property(device_class, PROPERTY_DVDRW_UMOUNT_COMMAND,
283         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
284         device_simple_property_get_fn,
285         dvdrw_device_set_umount_command_fn);
286 }
287
288 static void
289 dvdrw_device_init (DvdRwDevice *self)
290 {
291     Device *dself = DEVICE(self);
292     GValue val;
293
294     self->dvdrw_device = NULL;
295     self->cache_dir = NULL;
296     self->cache_data = NULL;
297     self->mount_point = NULL;
298     self->mount_data = NULL;
299     self->mounted = FALSE;
300     self->keep_cache = FALSE;
301     self->growisofs_command = NULL;
302     self->mount_command = NULL;
303     self->umount_command = NULL;
304
305     bzero(&val, sizeof(val));
306
307     g_value_init(&val, G_TYPE_BOOLEAN);
308     g_value_set_boolean(&val, FALSE);
309     device_set_simple_property(dself, PROPERTY_APPENDABLE,
310         &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
311     g_value_unset(&val);
312
313     g_value_init(&val, G_TYPE_BOOLEAN);
314     g_value_set_boolean(&val, FALSE);
315     device_set_simple_property(dself, PROPERTY_PARTIAL_DELETION,
316         &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
317     g_value_unset(&val);
318
319     g_value_init(&val, G_TYPE_BOOLEAN);
320     g_value_set_boolean(&val, FALSE);
321     device_set_simple_property(dself, PROPERTY_FULL_DELETION,
322         &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
323     g_value_unset(&val);
324 }
325
326 static gboolean
327 dvdrw_device_set_mount_point_fn(Device *dself, DevicePropertyBase *base,
328     GValue *val, PropertySurety surety, PropertySource source)
329 {
330     DvdRwDevice *self = DVDRW_DEVICE(dself);
331
332     amfree(self->mount_point);
333     amfree(self->mount_data);
334
335     self->mount_point = g_value_dup_string(val);
336     self->mount_data = g_strconcat(self->mount_point, "/data/", NULL);
337
338     device_clear_volume_details(dself);
339
340     return device_simple_property_set_fn(dself, base, val, surety, source);
341 }
342
343 static gboolean
344 dvdrw_device_set_keep_cache_fn(Device *dself, DevicePropertyBase *base,
345     GValue *val, PropertySurety surety, PropertySource source)
346 {
347     DvdRwDevice *self = DVDRW_DEVICE(dself);
348
349     self->keep_cache = g_value_get_boolean(val);
350
351     return device_simple_property_set_fn(dself, base, val, surety, source);
352 }
353
354 static gboolean
355 dvdrw_device_set_unlabelled_when_unmountable_fn(Device *dself, DevicePropertyBase *base,
356     GValue *val, PropertySurety surety, PropertySource source)
357 {
358     DvdRwDevice *self = DVDRW_DEVICE(dself);
359
360     self->unlabelled_when_unmountable = g_value_get_boolean(val);
361
362     return device_simple_property_set_fn(dself, base, val, surety, source);
363 }
364
365 static gboolean
366 dvdrw_device_set_growisofs_command_fn(Device *dself, DevicePropertyBase *base,
367     GValue *val, PropertySurety surety, PropertySource source)
368 {
369     DvdRwDevice *self = DVDRW_DEVICE(dself);
370
371     self->growisofs_command = g_value_dup_string(val);
372
373     return device_simple_property_set_fn(dself, base, val, surety, source);
374 }
375
376 static gboolean
377 dvdrw_device_set_mount_command_fn(Device *dself, DevicePropertyBase *base,
378     GValue *val, PropertySurety surety, PropertySource source)
379 {
380     DvdRwDevice *self = DVDRW_DEVICE(dself);
381
382     self->mount_command = g_value_dup_string(val);
383
384     return device_simple_property_set_fn(dself, base, val, surety, source);
385 }
386
387 static gboolean
388 dvdrw_device_set_umount_command_fn(Device *dself, DevicePropertyBase *base,
389     GValue *val, PropertySurety surety, PropertySource source)
390 {
391     DvdRwDevice *self = DVDRW_DEVICE(dself);
392
393     self->umount_command = g_value_dup_string(val);
394
395     return device_simple_property_set_fn(dself, base, val, surety, source);
396 }
397
398 static void
399 dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node)
400 {
401     DvdRwDevice *self = DVDRW_DEVICE(dself);
402     DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
403     GValue val;
404     char *colon;
405
406     g_debug("Opening device: %s", device_node);
407
408     bzero(&val, sizeof(val));
409
410     colon = index(device_node, ':');
411     if (!colon) {
412         device_set_error(dself,
413             stralloc(_("DVDRW device requires cache directory and DVD-RW device separated by a colon (:) in tapedev")),
414             DEVICE_STATUS_DEVICE_ERROR);
415         return;
416     }
417
418     self->cache_dir = g_strndup(device_node, colon - device_node);
419     self->cache_data = g_strconcat(self->cache_dir, "/data/", NULL);
420     self->dvdrw_device = g_strdup(colon + 1);
421
422     parent_class->open_device(dself, device_name, device_type, device_node);
423 }
424
425 static DeviceStatusFlags
426 dvdrw_device_read_label(Device *dself)
427 {
428     DvdRwDevice *self = DVDRW_DEVICE(dself);
429     VfsDevice *vself = VFS_DEVICE(dself);
430     gboolean mounted = FALSE;
431     DeviceStatusFlags status;
432     struct stat dir_status;
433     DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
434
435     g_debug("Reading label from media at %s", self->mount_point);
436
437     if (device_in_error(dself)) return DEVICE_STATUS_DEVICE_ERROR;
438     if (!check_readable(self)) return DEVICE_STATUS_DEVICE_ERROR;
439
440     if (!self->mounted) {
441         status = mount_disc(self, !self->unlabelled_when_unmountable);
442         if (status != DEVICE_STATUS_SUCCESS) {
443             /* Not mountable. May be freshly formatted or corrupted, drive may be empty. */
444             return (self->unlabelled_when_unmountable)
445                 ? DEVICE_STATUS_VOLUME_UNLABELED
446                 : status;
447         }
448         mounted = TRUE;
449     }
450
451     if ((stat(self->mount_data, &dir_status) < 0) && (errno == ENOENT)) {
452         /* No data directory, consider the DVD unlabelled */
453         g_debug("Media contains no data directory and therefore no label");
454         unmount_disc(self);
455
456         return DEVICE_STATUS_VOLUME_UNLABELED;
457     }
458
459     amfree(vself->dir_name);
460     vself->dir_name = g_strdup(self->mount_data);
461     status = parent_class->read_label(dself);
462
463     if (mounted) {
464         unmount_disc(self);
465     }
466
467     return status;
468 }
469
470 static gboolean
471 dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp)
472 {
473     DvdRwDevice *self = DVDRW_DEVICE(dself);
474     VfsDevice *vself = VFS_DEVICE(dself);
475     DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
476
477     g_debug("Start DVDRW device");
478
479     if (device_in_error(dself)) return FALSE;
480     if (!check_access_mode(self, mode)) return FALSE;
481
482     dself->access_mode = mode;
483
484     /* We'll replace this with our own value */
485     amfree(vself->dir_name);
486
487     if (mode == ACCESS_READ) {
488         if (mount_disc(self, TRUE) != DEVICE_STATUS_SUCCESS) {
489             return FALSE;
490         }
491
492         vself->dir_name = g_strdup(self->mount_data);
493     } else if (mode == ACCESS_WRITE) {
494         vself->dir_name = g_strdup(self->cache_data);
495     }
496
497     return parent_class->start(dself, mode, label, timestamp);
498 }
499
500 static gboolean
501 dvdrw_device_finish(Device *dself)
502 {
503     DvdRwDevice *self = DVDRW_DEVICE(dself);
504     VfsDevice *vself = VFS_DEVICE(dself);
505     gboolean result;
506     DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
507     DeviceAccessMode mode;
508
509     g_debug("Finish DVDRW device");
510
511     /* Save access mode before parent class messes with it */
512     mode = dself->access_mode;
513
514     if (device_in_error(dself)) {
515         if (mode == ACCESS_READ) {
516             /* Still need to do this, don't care if it works or not */
517             unmount_disc(self);
518         }
519
520         return FALSE;
521     }
522
523     result = parent_class->finish(dself);
524
525     if (mode == ACCESS_READ) {
526         unmount_disc(self);
527     }
528
529     if (! result) {
530         return FALSE;
531     }
532
533     if (mode == ACCESS_WRITE) {
534         result = burn_disc(self);
535
536         if (result && !self->keep_cache) {
537             delete_vfs_files(vself);
538         }
539
540         return result;
541     }
542
543     return TRUE;
544 }
545
546 static gboolean
547 burn_disc(DvdRwDevice *self)
548 {
549     gint status;
550
551     char *burn_argv[] = {NULL, "-use-the-force-luke",
552         "-Z", self->dvdrw_device,
553         "-J", "-R", "-pad", "-quiet",
554         self->cache_dir, NULL};
555
556     if (self->growisofs_command == NULL) {
557         burn_argv[0] = "growisofs";
558     } else {
559         burn_argv[0] = self->growisofs_command;
560     }
561
562     g_debug("Burning media in %s", self->dvdrw_device);
563     if (execute_command(self, burn_argv, &status) != DEVICE_STATUS_SUCCESS) {
564         return FALSE;
565     }
566     g_debug("Burn completed successfully");
567
568     return TRUE;
569 }
570
571 static void
572 dvdrw_device_finalize(GObject *gself)
573 {
574     DvdRwDevice *self = DVDRW_DEVICE(gself);
575     GObjectClass *parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(gself)));
576
577     if (parent_class->finalize) {
578         parent_class->finalize(gself);
579     }
580
581     amfree(self->dvdrw_device);
582
583     amfree(self->cache_dir);
584     amfree(self->cache_data);
585     amfree(self->mount_point);
586     amfree(self->mount_data);
587     amfree(self->growisofs_command);
588     amfree(self->mount_command);
589     amfree(self->umount_command);
590 }
591
592 static gboolean
593 check_access_mode(DvdRwDevice *self, DeviceAccessMode mode)
594 {
595     Device *dself = DEVICE(self);
596
597     if (mode == ACCESS_READ) {
598         return check_readable(self);
599     } else if (mode == ACCESS_WRITE) {
600         return TRUE;
601     }
602
603     device_set_error(dself,
604         stralloc(_("DVDRW device can only be opened in READ or WRITE mode")),
605         DEVICE_STATUS_DEVICE_ERROR);
606
607     return FALSE;
608 }
609
610 static gboolean
611 check_readable(DvdRwDevice *self)
612 {
613     Device *dself = DEVICE(self);
614     GValue value;
615     bzero(&value, sizeof(value));
616
617     if (! device_get_simple_property(dself, PROPERTY_DVDRW_MOUNT_POINT, &value, NULL, NULL)) {
618         device_set_error(dself,
619             stralloc(_("DVDRW device requires DVDRW_MOUNT_POINT to open device for reading")),
620             DEVICE_STATUS_DEVICE_ERROR);
621
622         return FALSE;
623     }
624
625     return TRUE;
626 }
627
628 static DeviceStatusFlags
629 mount_disc(DvdRwDevice *self, gboolean report_error)
630 {
631     Device *dself = DEVICE(self);
632     gchar *mount_argv[] = { NULL, self->mount_point, NULL };
633     DeviceStatusFlags status;
634
635     if (self->mounted) {
636         return DEVICE_STATUS_SUCCESS;
637     }
638
639     if (self->mount_command == NULL) {
640         mount_argv[0] = "mount";
641     } else {
642         mount_argv[0] = self->mount_command;
643     }
644
645     g_debug("Mounting media at %s", self->mount_point);
646     status = execute_command(report_error ? self : NULL, mount_argv, NULL);
647     if (status != DEVICE_STATUS_SUCCESS) {
648         /* Wait a few seconds and try again - The tray may still be out after burning */
649         sleep(3);
650         if (execute_command(report_error ? self : NULL, mount_argv, NULL) == DEVICE_STATUS_SUCCESS) {
651             /* Clear error */
652             device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
653             self->mounted = TRUE;
654             return DEVICE_STATUS_SUCCESS;
655         } else {
656             return status;
657         }
658     }
659
660     self->mounted = TRUE;
661     return DEVICE_STATUS_SUCCESS;
662 }
663
664 static void
665 unmount_disc(DvdRwDevice *self)
666 {
667     gchar *unmount_argv[] = { NULL, self->mount_point, NULL };
668     DeviceStatusFlags status;
669
670     if (! self->mounted) {
671         return;
672     }
673
674     if (self->umount_command == NULL) {
675         unmount_argv[0] = "umount";
676     } else {
677         unmount_argv[0] = self->umount_command;
678     }
679
680     g_debug("Unmounting media at %s", self->mount_point);
681     status = execute_command(NULL, unmount_argv, NULL);
682     if (status == DEVICE_STATUS_SUCCESS) {
683         self->mounted = FALSE;
684     }
685 }
686
687 static DeviceStatusFlags
688 execute_command(DvdRwDevice *self, gchar **argv, gint *result)
689 {
690     Device *dself = DEVICE(self);
691     gchar *std_output = NULL;
692     gchar *std_error = NULL;
693     gint errnum = 0;
694     GError *error = NULL;
695     gboolean success;
696     int signum = 0;
697
698     /* g_debug("Executing: %s", argv[0]); */
699
700     g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
701         &std_output, &std_error, &errnum, &error);
702
703     /* g_debug("Execution complete"); */
704
705     if (WIFSIGNALED(errnum)) {
706         success = FALSE;
707         signum = WTERMSIG(errnum);
708     } else if (WIFEXITED(errnum)) {
709         success = (WEXITSTATUS(errnum) == 0);
710     } else {
711         success = FALSE;
712     }
713
714     if (!success) {
715         gchar *error_message = vstrallocf(_("DVDRW device cannot execute '%s': %s (status: %d) (stderr: %s)"),
716             argv[0], error ? error->message : _("Unknown error"), errnum, std_error ? std_error: "No stderr");
717
718         if (dself != NULL) {
719             device_set_error(dself, error_message, DEVICE_STATUS_DEVICE_ERROR);
720         }
721
722         if (std_output) {
723             g_free(std_output);
724         }
725
726         if (std_error) {
727             g_free(std_error);
728         }
729
730         if (error) {
731             g_error_free(error);
732         }
733
734         if (result != NULL) {
735             *result = errnum;
736         }
737
738         return DEVICE_STATUS_DEVICE_ERROR;
739     }
740
741     return DEVICE_STATUS_SUCCESS;
742 }