]> git.ozlabs.org Git - petitboot/blob - discover/device-handler.c
discover: move device sources to the device handler
[petitboot] / discover / device-handler.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <stdbool.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <mntent.h>
8 #include <sys/stat.h>
9 #include <sys/wait.h>
10 #include <sys/mount.h>
11
12 #include <talloc/talloc.h>
13 #include <list/list.h>
14 #include <log/log.h>
15 #include <types/types.h>
16 #include <system/system.h>
17 #include <process/process.h>
18 #include <url/url.h>
19
20 #include "device-handler.h"
21 #include "discover-server.h"
22 #include "user-event.h"
23 #include "platform.h"
24 #include "event.h"
25 #include "parser.h"
26 #include "resource.h"
27 #include "paths.h"
28 #include "sysinfo.h"
29 #include "boot.h"
30 #include "udev.h"
31 #include "network.h"
32
33 struct device_handler {
34         struct discover_server  *server;
35         int                     dry_run;
36
37         struct pb_udev          *udev;
38         struct network          *network;
39         struct user_event       *user_event;
40
41         struct discover_device  **devices;
42         unsigned int            n_devices;
43
44         struct waitset          *waitset;
45         struct waiter           *timeout_waiter;
46         bool                    autoboot_enabled;
47         unsigned int            sec_to_boot;
48
49         struct discover_boot_option *default_boot_option;
50         struct list             unresolved_boot_options;
51
52         struct boot_task        *pending_boot;
53         bool                    pending_boot_is_default;
54 };
55
56 static int mount_device(struct discover_device *dev);
57 static int umount_device(struct discover_device *dev);
58
59 static int device_handler_init_sources(struct device_handler *handler);
60
61 void discover_context_add_boot_option(struct discover_context *ctx,
62                 struct discover_boot_option *boot_option)
63 {
64         boot_option->source = ctx->parser;
65         list_add_tail(&ctx->boot_options, &boot_option->list);
66         talloc_steal(ctx, boot_option);
67 }
68
69 /**
70  * device_handler_get_device_count - Get the count of current handler devices.
71  */
72
73 int device_handler_get_device_count(const struct device_handler *handler)
74 {
75         return handler->n_devices;
76 }
77
78 /**
79  * device_handler_get_device - Get a handler device by index.
80  */
81
82 const struct discover_device *device_handler_get_device(
83         const struct device_handler *handler, unsigned int index)
84 {
85         if (index >= handler->n_devices) {
86                 assert(0 && "bad index");
87                 return NULL;
88         }
89
90         return handler->devices[index];
91 }
92
93 struct discover_boot_option *discover_boot_option_create(
94                 struct discover_context *ctx,
95                 struct discover_device *device)
96 {
97         struct discover_boot_option *opt;
98
99         opt = talloc_zero(ctx, struct discover_boot_option);
100         opt->option = talloc_zero(opt, struct boot_option);
101         opt->device = device;
102
103         return opt;
104 }
105
106 static int device_match_uuid(struct discover_device *dev, const char *uuid)
107 {
108         return dev->uuid && !strcmp(dev->uuid, uuid);
109 }
110
111 static int device_match_label(struct discover_device *dev, const char *label)
112 {
113         return dev->label && !strcmp(dev->label, label);
114 }
115
116 static int device_match_id(struct discover_device *dev, const char *id)
117 {
118         return !strcmp(dev->device->id, id);
119 }
120
121 static int device_match_serial(struct discover_device *dev, const char *serial)
122 {
123         const char *val = discover_device_get_param(dev, "ID_SERIAL");
124         return val && !strcmp(val, serial);
125 }
126
127 static struct discover_device *device_lookup(
128                 struct device_handler *device_handler,
129                 int (match_fn)(struct discover_device *, const char *),
130                 const char *str)
131 {
132         struct discover_device *dev;
133         unsigned int i;
134
135         if (!str)
136                 return NULL;
137
138         for (i = 0; i < device_handler->n_devices; i++) {
139                 dev = device_handler->devices[i];
140
141                 if (match_fn(dev, str))
142                         return dev;
143         }
144
145         return NULL;
146 }
147
148 struct discover_device *device_lookup_by_name(struct device_handler *handler,
149                 const char *name)
150 {
151         if (!strncmp(name, "/dev/", strlen("/dev/")))
152                 name += strlen("/dev/");
153
154         return device_lookup_by_id(handler, name);
155 }
156
157 struct discover_device *device_lookup_by_uuid(
158                 struct device_handler *device_handler,
159                 const char *uuid)
160 {
161         return device_lookup(device_handler, device_match_uuid, uuid);
162 }
163
164 struct discover_device *device_lookup_by_label(
165                 struct device_handler *device_handler,
166                 const char *label)
167 {
168         return device_lookup(device_handler, device_match_label, label);
169 }
170
171 struct discover_device *device_lookup_by_id(
172                 struct device_handler *device_handler,
173                 const char *id)
174 {
175         return device_lookup(device_handler, device_match_id, id);
176 }
177
178 struct discover_device *device_lookup_by_serial(
179                 struct device_handler *device_handler,
180                 const char *serial)
181 {
182         return device_lookup(device_handler, device_match_serial, serial);
183 }
184
185 void device_handler_destroy(struct device_handler *handler)
186 {
187         talloc_free(handler);
188 }
189
190 static int destroy_device(void *arg)
191 {
192         struct discover_device *dev = arg;
193
194         umount_device(dev);
195
196         return 0;
197 }
198
199 struct discover_device *discover_device_create(struct device_handler *handler,
200                 const char *id)
201 {
202         struct discover_device *dev;
203
204         dev = device_lookup_by_id(handler, id);
205         if (dev)
206                 return dev;
207
208         dev = talloc_zero(handler, struct discover_device);
209         dev->device = talloc_zero(dev, struct device);
210         dev->device->id = talloc_strdup(dev->device, id);
211         list_init(&dev->params);
212         list_init(&dev->boot_options);
213
214         talloc_set_destructor(dev, destroy_device);
215
216         return dev;
217 }
218
219 struct discover_device_param {
220         char                    *name;
221         char                    *value;
222         struct list_item        list;
223 };
224
225 void discover_device_set_param(struct discover_device *device,
226                 const char *name, const char *value)
227 {
228         struct discover_device_param *param;
229         bool found = false;
230
231         list_for_each_entry(&device->params, param, list) {
232                 if (!strcmp(param->name, name)) {
233                         found = true;
234                         break;
235                 }
236         }
237
238         if (!found) {
239                 if (!value)
240                         return;
241                 param = talloc(device, struct discover_device_param);
242                 param->name = talloc_strdup(param, name);
243                 list_add(&device->params, &param->list);
244         } else {
245                 if (!value) {
246                         list_remove(&param->list);
247                         talloc_free(param);
248                         return;
249                 }
250                 talloc_free(param->value);
251         }
252
253         param->value = talloc_strdup(param, value);
254 }
255
256 const char *discover_device_get_param(struct discover_device *device,
257                 const char *name)
258 {
259         struct discover_device_param *param;
260
261         list_for_each_entry(&device->params, param, list) {
262                 if (!strcmp(param->name, name))
263                         return param->value;
264         }
265         return NULL;
266 }
267
268 struct device_handler *device_handler_init(struct discover_server *server,
269                 struct waitset *waitset, int dry_run)
270 {
271         struct device_handler *handler;
272         int rc;
273
274         handler = talloc_zero(NULL, struct device_handler);
275         handler->server = server;
276         handler->waitset = waitset;
277         handler->dry_run = dry_run;
278         handler->autoboot_enabled = config_get()->autoboot_enabled;
279
280         list_init(&handler->unresolved_boot_options);
281
282         /* set up our mount point base */
283         pb_mkdir_recursive(mount_base());
284
285         parser_init();
286
287         rc = device_handler_init_sources(handler);
288         if (rc) {
289                 talloc_free(handler);
290                 return NULL;
291         }
292
293         return handler;
294 }
295
296 void device_handler_remove(struct device_handler *handler,
297                 struct discover_device *device)
298 {
299         struct discover_boot_option *opt, *tmp;
300         unsigned int i;
301
302         for (i = 0; i < handler->n_devices; i++)
303                 if (handler->devices[i] == device)
304                         break;
305
306         if (i == handler->n_devices) {
307                 talloc_free(device);
308                 return;
309         }
310
311         /* Free any unresolved options, as they're currently allocated
312          * against the handler */
313         list_for_each_entry_safe(&handler->unresolved_boot_options,
314                         opt, tmp, list) {
315                 if (opt->device != device)
316                         continue;
317                 list_remove(&opt->list);
318                 talloc_free(opt);
319         }
320
321         handler->n_devices--;
322         memmove(&handler->devices[i], &handler->devices[i + 1],
323                 (handler->n_devices - i) * sizeof(handler->devices[0]));
324         handler->devices = talloc_realloc(handler, handler->devices,
325                 struct discover_device *, handler->n_devices);
326
327         if (device->notified)
328                 discover_server_notify_device_remove(handler->server,
329                                                         device->device);
330
331         talloc_free(device);
332 }
333
334 static void boot_status(void *arg, struct boot_status *status)
335 {
336         struct device_handler *handler = arg;
337
338         discover_server_notify_boot_status(handler->server, status);
339 }
340
341 static void countdown_status(struct device_handler *handler,
342                 struct discover_boot_option *opt, unsigned int sec)
343 {
344         struct boot_status status;
345
346         status.type = BOOT_STATUS_INFO;
347         status.progress = -1;
348         status.detail = NULL;
349         status.message = talloc_asprintf(handler,
350                         "Booting %s in %u sec", opt->option->name, sec);
351
352         discover_server_notify_boot_status(handler->server, &status);
353
354         talloc_free(status.message);
355 }
356
357 static int default_timeout(void *arg)
358 {
359         struct device_handler *handler = arg;
360         struct discover_boot_option *opt;
361
362         if (!handler->default_boot_option)
363                 return 0;
364
365         if (handler->pending_boot)
366                 return 0;
367
368         opt = handler->default_boot_option;
369
370         if (handler->sec_to_boot) {
371                 countdown_status(handler, opt, handler->sec_to_boot);
372                 handler->sec_to_boot--;
373                 handler->timeout_waiter = waiter_register_timeout(
374                                                 handler->waitset, 1000,
375                                                 default_timeout, handler);
376                 return 0;
377         }
378
379         handler->timeout_waiter = NULL;
380
381         pb_log("Timeout expired, booting default option %s\n", opt->option->id);
382
383         handler->pending_boot = boot(handler, handler->default_boot_option,
384                         NULL, handler->dry_run, boot_status, handler);
385         handler->pending_boot_is_default = true;
386         return 0;
387 }
388
389 static bool priority_match(struct boot_priority *prio,
390                 struct discover_boot_option *opt)
391 {
392         return prio->type == opt->device->device->type ||
393                 prio->type == DEVICE_TYPE_ANY;
394 }
395
396 static int default_option_priority(struct discover_boot_option *opt)
397 {
398         const struct config *config;
399         struct boot_priority *prio;
400         unsigned int i;
401
402         config = config_get();
403
404         for (i = 0; i < config->n_boot_priorities; i++) {
405                 prio = &config->boot_priorities[i];
406                 if (priority_match(prio, opt))
407                         return prio->priority;
408         }
409
410         return 0;
411 }
412
413 static void set_default(struct device_handler *handler,
414                 struct discover_boot_option *opt)
415 {
416         int new_prio;
417
418         if (!handler->autoboot_enabled)
419                 return;
420
421         new_prio = default_option_priority(opt);
422
423         /* A negative priority indicates that we don't want to boot this device
424          * by default */
425         if (new_prio < 0)
426                 return;
427
428         /* Resolve any conflicts: if we have a new default option, it only
429          * replaces the current if it has a higher priority. */
430         if (handler->default_boot_option) {
431                 int cur_prio;
432
433                 cur_prio = default_option_priority(
434                                         handler->default_boot_option);
435
436                 if (new_prio > cur_prio) {
437                         handler->default_boot_option = opt;
438                         /* extend the timeout a little, so the user sees some
439                          * indication of the change */
440                         handler->sec_to_boot += 2;
441                 }
442
443                 return;
444         }
445
446         handler->sec_to_boot = config_get()->autoboot_timeout_sec;
447         handler->default_boot_option = opt;
448
449         pb_log("Boot option %s set as default, timeout %u sec.\n",
450                opt->option->id, handler->sec_to_boot);
451
452         default_timeout(handler);
453 }
454
455 static bool resource_is_resolved(struct resource *res)
456 {
457         return !res || res->resolved;
458 }
459
460 /* We only use this in an assert, which will disappear if we're compiling
461  * with NDEBUG, so we need the 'used' attribute for these builds */
462 static bool __attribute__((used)) boot_option_is_resolved(
463                 struct discover_boot_option *opt)
464 {
465         return resource_is_resolved(opt->boot_image) &&
466                 resource_is_resolved(opt->initrd) &&
467                 resource_is_resolved(opt->dtb) &&
468                 resource_is_resolved(opt->icon);
469 }
470
471 static bool resource_resolve(struct resource *res, const char *name,
472                 struct discover_boot_option *opt,
473                 struct device_handler *handler)
474 {
475         struct parser *parser = opt->source;
476
477         if (resource_is_resolved(res))
478                 return true;
479
480         pb_debug("Attempting to resolve resource %s->%s with parser %s\n",
481                         opt->option->id, name, parser->name);
482         parser->resolve_resource(handler, res);
483
484         return res->resolved;
485 }
486
487 static bool boot_option_resolve(struct discover_boot_option *opt,
488                 struct device_handler *handler)
489 {
490         return resource_resolve(opt->boot_image, "boot_image", opt, handler) &&
491                 resource_resolve(opt->initrd, "initrd", opt, handler) &&
492                 resource_resolve(opt->dtb, "dtb", opt, handler) &&
493                 resource_resolve(opt->icon, "icon", opt, handler);
494 }
495
496 static void boot_option_finalise(struct device_handler *handler,
497                 struct discover_boot_option *opt)
498 {
499         assert(boot_option_is_resolved(opt));
500
501         /* check that the parsers haven't set any of the final data */
502         assert(!opt->option->boot_image_file);
503         assert(!opt->option->initrd_file);
504         assert(!opt->option->dtb_file);
505         assert(!opt->option->icon_file);
506         assert(!opt->option->device_id);
507
508         if (opt->boot_image)
509                 opt->option->boot_image_file = opt->boot_image->url->full;
510         if (opt->initrd)
511                 opt->option->initrd_file = opt->initrd->url->full;
512         if (opt->dtb)
513                 opt->option->dtb_file = opt->dtb->url->full;
514         if (opt->icon)
515                 opt->option->icon_file = opt->icon->url->full;
516
517         opt->option->device_id = opt->device->device->id;
518
519         if (opt->option->is_default)
520                 set_default(handler, opt);
521 }
522
523 static void notify_boot_option(struct device_handler *handler,
524                 struct discover_boot_option *opt)
525 {
526         struct discover_device *dev = opt->device;
527
528         if (!dev->notified)
529                 discover_server_notify_device_add(handler->server,
530                                                   opt->device->device);
531         dev->notified = true;
532         discover_server_notify_boot_option_add(handler->server, opt->option);
533 }
534
535 static void process_boot_option_queue(struct device_handler *handler)
536 {
537         struct discover_boot_option *opt, *tmp;
538
539         list_for_each_entry_safe(&handler->unresolved_boot_options,
540                         opt, tmp, list) {
541
542                 pb_debug("queue: attempting resolution for %s\n",
543                                 opt->option->id);
544
545                 if (!boot_option_resolve(opt, handler))
546                         continue;
547
548                 pb_debug("\tresolved!\n");
549
550                 list_remove(&opt->list);
551                 list_add_tail(&opt->device->boot_options, &opt->list);
552                 talloc_steal(opt->device, opt);
553                 boot_option_finalise(handler, opt);
554                 notify_boot_option(handler, opt);
555         }
556 }
557
558 struct discover_context *device_handler_discover_context_create(
559                 struct device_handler *handler,
560                 struct discover_device *device)
561 {
562         struct discover_context *ctx;
563
564         ctx = talloc_zero(handler, struct discover_context);
565         ctx->device = device;
566         list_init(&ctx->boot_options);
567
568         return ctx;
569 }
570
571 /**
572  * context_commit - Commit a temporary discovery context to the handler,
573  * and notify the clients about any new options / devices
574  */
575 void device_handler_discover_context_commit(struct device_handler *handler,
576                 struct discover_context *ctx)
577 {
578         struct discover_device *dev = ctx->device;
579         struct discover_boot_option *opt, *tmp;
580
581         if (!device_lookup_by_id(handler, dev->device->id))
582                 device_handler_add_device(handler, dev);
583
584         /* move boot options from the context to the device */
585         list_for_each_entry_safe(&ctx->boot_options, opt, tmp, list) {
586                 list_remove(&opt->list);
587
588                 if (boot_option_resolve(opt, handler)) {
589                         pb_log("boot option %s is resolved, "
590                                         "sending to clients\n",
591                                         opt->option->id);
592                         list_add_tail(&dev->boot_options, &opt->list);
593                         talloc_steal(dev, opt);
594                         boot_option_finalise(handler, opt);
595                         notify_boot_option(handler, opt);
596                 } else {
597                         if (!opt->source->resolve_resource) {
598                                 pb_log("parser %s gave us an unresolved "
599                                         "resource (%s), but no way to "
600                                         "resolve it\n",
601                                         opt->source->name, opt->option->id);
602                                 talloc_free(opt);
603                         } else {
604                                 pb_log("boot option %s is unresolved, "
605                                                 "adding to queue\n",
606                                                 opt->option->id);
607                                 list_add(&handler->unresolved_boot_options,
608                                                 &opt->list);
609                                 talloc_steal(handler, opt);
610                         }
611                 }
612         }
613 }
614
615 void device_handler_add_device(struct device_handler *handler,
616                 struct discover_device *device)
617 {
618         handler->n_devices++;
619         handler->devices = talloc_realloc(handler, handler->devices,
620                                 struct discover_device *, handler->n_devices);
621         handler->devices[handler->n_devices - 1] = device;
622
623 }
624
625 /* Start discovery on a hotplugged device. The device will be in our devices
626  * array, but has only just been initialised by the hotplug source.
627  */
628 int device_handler_discover(struct device_handler *handler,
629                 struct discover_device *dev)
630 {
631         struct discover_context *ctx;
632         int rc;
633
634         process_boot_option_queue(handler);
635
636         /* create our context */
637         ctx = device_handler_discover_context_create(handler, dev);
638
639         rc = mount_device(dev);
640         if (rc)
641                 goto out;
642
643         /* add this device to our system info */
644         system_info_register_blockdev(dev->device->id, dev->uuid,
645                         dev->mount_path);
646
647         /* run the parsers. This will populate the ctx's boot_option list. */
648         iterate_parsers(ctx);
649
650         /* add discovered stuff to the handler */
651         device_handler_discover_context_commit(handler, ctx);
652
653 out:
654         talloc_free(ctx);
655
656         return 0;
657 }
658
659 /* Incoming dhcp event */
660 int device_handler_dhcp(struct device_handler *handler,
661                 struct discover_device *dev, struct event *event)
662 {
663         struct discover_context *ctx;
664
665         /* create our context */
666         ctx = device_handler_discover_context_create(handler, dev);
667         ctx->event = event;
668
669         iterate_parsers(ctx);
670
671         device_handler_discover_context_commit(handler, ctx);
672
673         talloc_free(ctx);
674
675         return 0;
676 }
677
678 /* incoming conf event */
679 int device_handler_conf(struct device_handler *handler,
680                 struct discover_device *dev, struct pb_url *url)
681 {
682         struct discover_context *ctx;
683
684         /* create our context */
685         ctx = device_handler_discover_context_create(handler, dev);
686         ctx->conf_url = url;
687
688         iterate_parsers(ctx);
689
690         device_handler_discover_context_commit(handler, ctx);
691
692         talloc_free(ctx);
693
694         return 0;
695 }
696
697 static struct discover_boot_option *find_boot_option_by_id(
698                 struct device_handler *handler, const char *id)
699 {
700         unsigned int i;
701
702         for (i = 0; i < handler->n_devices; i++) {
703                 struct discover_device *dev = handler->devices[i];
704                 struct discover_boot_option *opt;
705
706                 list_for_each_entry(&dev->boot_options, opt, list)
707                         if (!strcmp(opt->option->id, id))
708                                 return opt;
709         }
710
711         return NULL;
712 }
713
714 void device_handler_boot(struct device_handler *handler,
715                 struct boot_command *cmd)
716 {
717         struct discover_boot_option *opt = NULL;
718
719         if (cmd->option_id && strlen(cmd->option_id))
720                 opt = find_boot_option_by_id(handler, cmd->option_id);
721
722         if (handler->pending_boot)
723                 boot_cancel(handler->pending_boot);
724         handler->pending_boot = boot(handler, opt, cmd, handler->dry_run,
725                         boot_status, handler);
726         handler->pending_boot_is_default = false;
727 }
728
729 void device_handler_cancel_default(struct device_handler *handler)
730 {
731         struct boot_status status;
732
733         if (handler->timeout_waiter)
734                 waiter_remove(handler->timeout_waiter);
735
736         handler->timeout_waiter = NULL;
737         handler->autoboot_enabled = false;
738
739         /* we only send status if we had a default boot option queued */
740         if (!handler->default_boot_option)
741                 return;
742
743         pb_log("Cancelling default boot option\n");
744
745         if (handler->pending_boot && handler->pending_boot_is_default) {
746                 boot_cancel(handler->pending_boot);
747                 handler->pending_boot = NULL;
748                 handler->pending_boot_is_default = false;
749         }
750
751         handler->default_boot_option = NULL;
752
753         status.type = BOOT_STATUS_INFO;
754         status.progress = -1;
755         status.detail = NULL;
756         status.message = "Default boot cancelled";
757
758         discover_server_notify_boot_status(handler->server, &status);
759 }
760
761 void device_handler_update_config(struct device_handler *handler,
762                 struct config *config)
763 {
764         config_set(config);
765         discover_server_notify_config(handler->server, config);
766 }
767
768 #ifndef PETITBOOT_TEST
769
770 static int device_handler_init_sources(struct device_handler *handler)
771 {
772         /* init our device sources: udev, network and user events */
773         handler->udev = udev_init(handler, handler->waitset);
774         if (!handler->udev)
775                 return -1;
776
777         handler->network = network_init(handler, handler->waitset,
778                         handler->dry_run);
779         if (!handler->network)
780                 return -1;
781
782         handler->user_event = user_event_init(handler, handler->waitset);
783         if (!handler->user_event)
784                 return -1;
785
786         return 0;
787 }
788
789 static bool check_existing_mount(struct discover_device *dev)
790 {
791         struct stat devstat, mntstat;
792         struct mntent *mnt;
793         FILE *fp;
794         int rc;
795
796         rc = stat(dev->device_path, &devstat);
797         if (rc) {
798                 pb_debug("%s: stat failed: %s\n", __func__, strerror(errno));
799                 return false;
800         }
801
802         if (!S_ISBLK(devstat.st_mode)) {
803                 pb_debug("%s: %s isn't a block device?\n", __func__,
804                                 dev->device_path);
805                 return false;
806         }
807
808         fp = fopen("/proc/self/mounts", "r");
809
810         for (;;) {
811                 mnt = getmntent(fp);
812                 if (!mnt)
813                         break;
814
815                 if (!mnt->mnt_fsname || mnt->mnt_fsname[0] != '/')
816                         continue;
817
818                 rc = stat(mnt->mnt_fsname, &mntstat);
819                 if (rc)
820                         continue;
821
822                 if (!S_ISBLK(mntstat.st_mode))
823                         continue;
824
825                 if (mntstat.st_rdev == devstat.st_rdev) {
826                         dev->mount_path = talloc_strdup(dev, mnt->mnt_dir);
827                         dev->mounted_rw = !!hasmntopt(mnt, "rw");
828                         dev->mounted = true;
829                         dev->unmount = false;
830
831                         pb_debug("%s: %s is already mounted (r%c) at %s\n",
832                                         __func__, dev->device_path,
833                                         dev->mounted_rw ? 'w' : 'o',
834                                         mnt->mnt_dir);
835                         break;
836                 }
837         }
838
839         fclose(fp);
840
841         return mnt != NULL;
842 }
843
844 static int mount_device(struct discover_device *dev)
845 {
846         const char *fstype;
847         int rc;
848
849         if (!dev->device_path)
850                 return -1;
851
852         if (dev->mounted)
853                 return 0;
854
855         if (check_existing_mount(dev))
856                 return 0;
857
858         fstype = discover_device_get_param(dev, "ID_FS_TYPE");
859         if (!fstype)
860                 return 0;
861
862         dev->mount_path = join_paths(dev, mount_base(),
863                                         dev->device_path);
864
865         if (pb_mkdir_recursive(dev->mount_path)) {
866                 pb_log("couldn't create mount directory %s: %s\n",
867                                 dev->mount_path, strerror(errno));
868                 goto err_free;
869         }
870
871         pb_log("mounting device %s read-only\n", dev->device_path);
872         errno = 0;
873         rc = mount(dev->device_path, dev->mount_path, fstype,
874                         MS_RDONLY | MS_SILENT, "");
875         if (!rc) {
876                 dev->mounted = true;
877                 dev->mounted_rw = false;
878                 dev->unmount = true;
879                 return 0;
880         }
881
882         pb_log("couldn't mount device %s: mount failed: %s\n",
883                         dev->device_path, strerror(errno));
884
885         pb_rmdir_recursive(mount_base(), dev->mount_path);
886 err_free:
887         talloc_free(dev->mount_path);
888         dev->mount_path = NULL;
889         return -1;
890 }
891
892 static int umount_device(struct discover_device *dev)
893 {
894         int rc;
895
896         if (!dev->mounted || !dev->unmount)
897                 return 0;
898
899         pb_log("unmounting device %s\n", dev->device_path);
900         rc = umount(dev->mount_path);
901         if (rc)
902                 return -1;
903
904         dev->mounted = false;
905
906         pb_rmdir_recursive(mount_base(), dev->mount_path);
907
908         talloc_free(dev->mount_path);
909         dev->mount_path = NULL;
910
911         return 0;
912 }
913
914 int device_request_write(struct discover_device *dev, bool *release)
915 {
916         int rc;
917
918         *release = false;
919
920         if (!dev->mounted)
921                 return -1;
922
923         if (dev->mounted_rw)
924                 return 0;
925
926         pb_log("remounting device %s read-write\n", dev->device_path);
927         rc = mount(dev->device_path, dev->mount_path, "",
928                         MS_REMOUNT | MS_SILENT, "");
929         if (rc)
930                 return -1;
931
932         dev->mounted_rw = true;
933         *release = true;
934         return 0;
935 }
936
937 void device_release_write(struct discover_device *dev, bool release)
938 {
939         if (!release)
940                 return;
941
942         pb_log("remounting device %s read-only\n", dev->device_path);
943         mount(dev->device_path, dev->mount_path, "",
944                         MS_REMOUNT | MS_RDONLY | MS_SILENT, "");
945         dev->mounted_rw = false;
946 }
947
948 #else
949
950 static int device_handler_init_sources(
951                 struct device_handler *handler __attribute__((unused)))
952 {
953         return 0;
954 }
955
956 static int umount_device(struct discover_device *dev __attribute__((unused)))
957 {
958         return 0;
959 }
960
961 static int __attribute__((unused)) mount_device(
962                 struct discover_device *dev __attribute__((unused)))
963 {
964         return 0;
965 }
966
967 int device_request_write(struct discover_device *dev __attribute__((unused)),
968                 bool *release)
969 {
970         *release = true;
971         return 0;
972 }
973
974 void device_release_write(struct discover_device *dev __attribute__((unused)),
975         bool release __attribute__((unused)))
976 {
977 }
978
979 #endif
980