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