]> git.ozlabs.org Git - petitboot/blob - discover/device-handler.c
ui/ncurses: Add nc-subset selection screen
[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 <locale.h>
9 #include <sys/stat.h>
10 #include <sys/wait.h>
11 #include <sys/mount.h>
12
13 #include <talloc/talloc.h>
14 #include <list/list.h>
15 #include <log/log.h>
16 #include <types/types.h>
17 #include <system/system.h>
18 #include <process/process.h>
19 #include <url/url.h>
20 #include <i18n/i18n.h>
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netdb.h>
25 #include <arpa/inet.h>
26
27 #include "device-handler.h"
28 #include "discover-server.h"
29 #include "user-event.h"
30 #include "platform.h"
31 #include "event.h"
32 #include "parser.h"
33 #include "resource.h"
34 #include "paths.h"
35 #include "sysinfo.h"
36 #include "boot.h"
37 #include "udev.h"
38 #include "network.h"
39 #include "ipmi.h"
40
41 enum default_priority {
42         DEFAULT_PRIORITY_REMOTE         = 1,
43         DEFAULT_PRIORITY_LOCAL_UUID     = 2,
44         DEFAULT_PRIORITY_LOCAL_FIRST    = 3,
45         DEFAULT_PRIORITY_LOCAL_LAST     = 0xfe,
46         DEFAULT_PRIORITY_DISABLED       = 0xff,
47 };
48
49 struct device_handler {
50         struct discover_server  *server;
51         int                     dry_run;
52
53         struct pb_udev          *udev;
54         struct network          *network;
55         struct user_event       *user_event;
56
57         struct discover_device  **devices;
58         unsigned int            n_devices;
59
60         struct waitset          *waitset;
61         struct waiter           *timeout_waiter;
62         bool                    autoboot_enabled;
63         unsigned int            sec_to_boot;
64
65         struct discover_boot_option *default_boot_option;
66         int                     default_boot_option_priority;
67
68         struct list             unresolved_boot_options;
69
70         struct boot_task        *pending_boot;
71         bool                    pending_boot_is_default;
72 };
73
74 static int mount_device(struct discover_device *dev);
75 static int umount_device(struct discover_device *dev);
76
77 static int device_handler_init_sources(struct device_handler *handler);
78 static void device_handler_reinit_sources(struct device_handler *handler);
79
80 static void device_handler_update_lang(const char *lang);
81
82 void discover_context_add_boot_option(struct discover_context *ctx,
83                 struct discover_boot_option *boot_option)
84 {
85         boot_option->source = ctx->parser;
86         list_add_tail(&ctx->boot_options, &boot_option->list);
87         talloc_steal(ctx, boot_option);
88 }
89
90 /**
91  * device_handler_get_device_count - Get the count of current handler devices.
92  */
93
94 int device_handler_get_device_count(const struct device_handler *handler)
95 {
96         return handler->n_devices;
97 }
98
99 /**
100  * device_handler_get_device - Get a handler device by index.
101  */
102
103 const struct discover_device *device_handler_get_device(
104         const struct device_handler *handler, unsigned int index)
105 {
106         if (index >= handler->n_devices) {
107                 assert(0 && "bad index");
108                 return NULL;
109         }
110
111         return handler->devices[index];
112 }
113
114 struct discover_boot_option *discover_boot_option_create(
115                 struct discover_context *ctx,
116                 struct discover_device *device)
117 {
118         struct discover_boot_option *opt;
119
120         opt = talloc_zero(ctx, struct discover_boot_option);
121         opt->option = talloc_zero(opt, struct boot_option);
122         opt->device = device;
123
124         return opt;
125 }
126
127 static int device_match_uuid(struct discover_device *dev, const char *uuid)
128 {
129         return dev->uuid && !strcmp(dev->uuid, uuid);
130 }
131
132 static int device_match_label(struct discover_device *dev, const char *label)
133 {
134         return dev->label && !strcmp(dev->label, label);
135 }
136
137 static int device_match_id(struct discover_device *dev, const char *id)
138 {
139         return !strcmp(dev->device->id, id);
140 }
141
142 static int device_match_serial(struct discover_device *dev, const char *serial)
143 {
144         const char *val = discover_device_get_param(dev, "ID_SERIAL");
145         return val && !strcmp(val, serial);
146 }
147
148 static struct discover_device *device_lookup(
149                 struct device_handler *device_handler,
150                 int (match_fn)(struct discover_device *, const char *),
151                 const char *str)
152 {
153         struct discover_device *dev;
154         unsigned int i;
155
156         if (!str)
157                 return NULL;
158
159         for (i = 0; i < device_handler->n_devices; i++) {
160                 dev = device_handler->devices[i];
161
162                 if (match_fn(dev, str))
163                         return dev;
164         }
165
166         return NULL;
167 }
168
169 struct discover_device *device_lookup_by_name(struct device_handler *handler,
170                 const char *name)
171 {
172         if (!strncmp(name, "/dev/", strlen("/dev/")))
173                 name += strlen("/dev/");
174
175         return device_lookup_by_id(handler, name);
176 }
177
178 struct discover_device *device_lookup_by_uuid(
179                 struct device_handler *device_handler,
180                 const char *uuid)
181 {
182         return device_lookup(device_handler, device_match_uuid, uuid);
183 }
184
185 struct discover_device *device_lookup_by_label(
186                 struct device_handler *device_handler,
187                 const char *label)
188 {
189         return device_lookup(device_handler, device_match_label, label);
190 }
191
192 struct discover_device *device_lookup_by_id(
193                 struct device_handler *device_handler,
194                 const char *id)
195 {
196         return device_lookup(device_handler, device_match_id, id);
197 }
198
199 struct discover_device *device_lookup_by_serial(
200                 struct device_handler *device_handler,
201                 const char *serial)
202 {
203         return device_lookup(device_handler, device_match_serial, serial);
204 }
205
206 void device_handler_destroy(struct device_handler *handler)
207 {
208         talloc_free(handler);
209 }
210
211 static int destroy_device(void *arg)
212 {
213         struct discover_device *dev = arg;
214
215         umount_device(dev);
216
217         return 0;
218 }
219
220 struct discover_device *discover_device_create(struct device_handler *handler,
221                 const char *id)
222 {
223         struct discover_device *dev;
224
225         dev = device_lookup_by_id(handler, id);
226         if (dev)
227                 return dev;
228
229         dev = talloc_zero(handler, struct discover_device);
230         dev->device = talloc_zero(dev, struct device);
231         dev->device->id = talloc_strdup(dev->device, id);
232         list_init(&dev->params);
233         list_init(&dev->boot_options);
234
235         talloc_set_destructor(dev, destroy_device);
236
237         return dev;
238 }
239
240 struct discover_device_param {
241         char                    *name;
242         char                    *value;
243         struct list_item        list;
244 };
245
246 void discover_device_set_param(struct discover_device *device,
247                 const char *name, const char *value)
248 {
249         struct discover_device_param *param;
250         bool found = false;
251
252         list_for_each_entry(&device->params, param, list) {
253                 if (!strcmp(param->name, name)) {
254                         found = true;
255                         break;
256                 }
257         }
258
259         if (!found) {
260                 if (!value)
261                         return;
262                 param = talloc(device, struct discover_device_param);
263                 param->name = talloc_strdup(param, name);
264                 list_add(&device->params, &param->list);
265         } else {
266                 if (!value) {
267                         list_remove(&param->list);
268                         talloc_free(param);
269                         return;
270                 }
271                 talloc_free(param->value);
272         }
273
274         param->value = talloc_strdup(param, value);
275 }
276
277 const char *discover_device_get_param(struct discover_device *device,
278                 const char *name)
279 {
280         struct discover_device_param *param;
281
282         list_for_each_entry(&device->params, param, list) {
283                 if (!strcmp(param->name, name))
284                         return param->value;
285         }
286         return NULL;
287 }
288
289 struct device_handler *device_handler_init(struct discover_server *server,
290                 struct waitset *waitset, int dry_run)
291 {
292         struct device_handler *handler;
293         int rc;
294
295         handler = talloc_zero(NULL, struct device_handler);
296         handler->server = server;
297         handler->waitset = waitset;
298         handler->dry_run = dry_run;
299         handler->autoboot_enabled = config_get()->autoboot_enabled;
300
301         list_init(&handler->unresolved_boot_options);
302
303         /* set up our mount point base */
304         pb_mkdir_recursive(mount_base());
305
306         parser_init();
307
308         if (config_get()->safe_mode)
309                 return handler;
310
311         rc = device_handler_init_sources(handler);
312         if (rc) {
313                 talloc_free(handler);
314                 return NULL;
315         }
316
317         return handler;
318 }
319
320 void device_handler_reinit(struct device_handler *handler)
321 {
322         struct discover_boot_option *opt, *tmp;
323         unsigned int i;
324
325         device_handler_cancel_default(handler);
326
327         /* free unresolved boot options */
328         list_for_each_entry_safe(&handler->unresolved_boot_options,
329                         opt, tmp, list)
330                 talloc_free(opt);
331         list_init(&handler->unresolved_boot_options);
332
333         /* drop all devices */
334         for (i = 0; i < handler->n_devices; i++)
335                 discover_server_notify_device_remove(handler->server,
336                                 handler->devices[i]->device);
337
338         talloc_free(handler->devices);
339         handler->devices = NULL;
340         handler->n_devices = 0;
341
342         device_handler_reinit_sources(handler);
343 }
344
345 void device_handler_remove(struct device_handler *handler,
346                 struct discover_device *device)
347 {
348         struct discover_boot_option *opt, *tmp;
349         unsigned int i;
350
351         for (i = 0; i < handler->n_devices; i++)
352                 if (handler->devices[i] == device)
353                         break;
354
355         if (i == handler->n_devices) {
356                 talloc_free(device);
357                 return;
358         }
359
360         /* Free any unresolved options, as they're currently allocated
361          * against the handler */
362         list_for_each_entry_safe(&handler->unresolved_boot_options,
363                         opt, tmp, list) {
364                 if (opt->device != device)
365                         continue;
366                 list_remove(&opt->list);
367                 talloc_free(opt);
368         }
369
370         /* if this is a network device, we have to unregister it from the
371          * network code */
372         if (device->device->type == DEVICE_TYPE_NETWORK)
373                 network_unregister_device(handler->network, device);
374
375         handler->n_devices--;
376         memmove(&handler->devices[i], &handler->devices[i + 1],
377                 (handler->n_devices - i) * sizeof(handler->devices[0]));
378         handler->devices = talloc_realloc(handler, handler->devices,
379                 struct discover_device *, handler->n_devices);
380
381         if (device->notified)
382                 discover_server_notify_device_remove(handler->server,
383                                                         device->device);
384
385         talloc_free(device);
386 }
387
388 static void boot_status(void *arg, struct boot_status *status)
389 {
390         struct device_handler *handler = arg;
391
392         discover_server_notify_boot_status(handler->server, status);
393 }
394
395 static void countdown_status(struct device_handler *handler,
396                 struct discover_boot_option *opt, unsigned int sec)
397 {
398         struct boot_status status;
399
400         status.type = BOOT_STATUS_INFO;
401         status.progress = -1;
402         status.detail = NULL;
403         status.message = talloc_asprintf(handler,
404                         _("Booting in %d sec: %s"), sec, opt->option->name);
405
406         discover_server_notify_boot_status(handler->server, &status);
407
408         talloc_free(status.message);
409 }
410
411 static int default_timeout(void *arg)
412 {
413         struct device_handler *handler = arg;
414         struct discover_boot_option *opt;
415
416         if (!handler->default_boot_option)
417                 return 0;
418
419         if (handler->pending_boot)
420                 return 0;
421
422         opt = handler->default_boot_option;
423
424         if (handler->sec_to_boot) {
425                 countdown_status(handler, opt, handler->sec_to_boot);
426                 handler->sec_to_boot--;
427                 handler->timeout_waiter = waiter_register_timeout(
428                                                 handler->waitset, 1000,
429                                                 default_timeout, handler);
430                 return 0;
431         }
432
433         handler->timeout_waiter = NULL;
434
435         pb_log("Timeout expired, booting default option %s\n", opt->option->id);
436
437         handler->pending_boot = boot(handler, handler->default_boot_option,
438                         NULL, handler->dry_run, boot_status, handler);
439         handler->pending_boot_is_default = true;
440         return 0;
441 }
442
443 struct {
444         enum ipmi_bootdev       ipmi_type;
445         enum device_type        device_type;
446 } device_type_map[] = {
447         { IPMI_BOOTDEV_NETWORK, DEVICE_TYPE_NETWORK },
448         { IPMI_BOOTDEV_DISK, DEVICE_TYPE_DISK },
449         { IPMI_BOOTDEV_CDROM, DEVICE_TYPE_OPTICAL },
450 };
451
452 static bool ipmi_device_type_matches(enum ipmi_bootdev ipmi_type,
453                 enum device_type device_type)
454 {
455         unsigned int i;
456
457         for (i = 0; i < ARRAY_SIZE(device_type_map); i++) {
458                 if (device_type_map[i].device_type == device_type)
459                         return device_type_map[i].ipmi_type == ipmi_type;
460         }
461
462         return false;
463 }
464
465 static bool priority_matches(struct boot_priority *prio,
466                 struct discover_boot_option *opt)
467 {
468         return prio->type == opt->device->device->type ||
469                 prio->type == DEVICE_TYPE_ANY;
470 }
471
472 /*
473  * We have different priorities to resolve conflicts between boot options that
474  * report to be the default for their device. This function assigns a priority
475  * for these options.
476  */
477 static enum default_priority default_option_priority(
478                 struct discover_boot_option *opt)
479 {
480         const struct config *config;
481         const char *dev_str;
482         unsigned int i;
483
484         config = config_get();
485
486         /* We give highest priority to IPMI-configured boot options. If
487          * we have an IPMI bootdev configuration set, then we don't allow
488          * any other defaults */
489         if (config->ipmi_bootdev) {
490                 bool ipmi_match = ipmi_device_type_matches(config->ipmi_bootdev,
491                                 opt->device->device->type);
492                 if (ipmi_match)
493                         return DEFAULT_PRIORITY_REMOTE;
494
495                 pb_debug("handler: disabled default priority due to "
496                                 "non-matching IPMI type %x\n",
497                                 config->ipmi_bootdev);
498                 return DEFAULT_PRIORITY_DISABLED;
499         }
500
501         /* Next, allow matching by device UUID. If we have one set but it
502          * doesn't match, disallow the default entirely */
503         dev_str = config->boot_device;
504         if (dev_str && dev_str[0]) {
505                 if (!strcmp(opt->device->uuid, dev_str))
506                         return DEFAULT_PRIORITY_LOCAL_UUID;
507
508                 pb_debug("handler: disabled default priority due to "
509                                 "non-matching UUID\n");
510                 return DEFAULT_PRIORITY_DISABLED;
511         }
512
513         /* Lastly, use the local priorities */
514         for (i = 0; i < config->n_boot_priorities; i++) {
515                 struct boot_priority *prio = &config->boot_priorities[i];
516                 if (priority_matches(prio, opt))
517                         return DEFAULT_PRIORITY_LOCAL_FIRST + prio->priority;
518         }
519
520         return DEFAULT_PRIORITY_DISABLED;
521 }
522
523 static void set_default(struct device_handler *handler,
524                 struct discover_boot_option *opt)
525 {
526         enum default_priority cur_prio, new_prio;
527
528         if (!handler->autoboot_enabled)
529                 return;
530
531         pb_debug("handler: new default option: %s\n", opt->option->id);
532
533         new_prio = default_option_priority(opt);
534
535         /* Anything outside our range prevents a default boot */
536         if (new_prio >= DEFAULT_PRIORITY_DISABLED)
537                 return;
538
539         pb_debug("handler: calculated priority %d\n", new_prio);
540
541         /* Resolve any conflicts: if we have a new default option, it only
542          * replaces the current if it has a higher priority. */
543         if (handler->default_boot_option) {
544
545                 cur_prio = handler->default_boot_option_priority;
546
547                 if (new_prio < cur_prio) {
548                         pb_log("handler: new prio %d beats "
549                                         "old prio %d for %s\n",
550                                         new_prio, cur_prio,
551                                         handler->default_boot_option
552                                                 ->option->id);
553                         handler->default_boot_option = opt;
554                         handler->default_boot_option_priority = new_prio;
555                         /* extend the timeout a little, so the user sees some
556                          * indication of the change */
557                         handler->sec_to_boot += 2;
558                 }
559
560                 return;
561         }
562
563         handler->sec_to_boot = config_get()->autoboot_timeout_sec;
564         handler->default_boot_option = opt;
565         handler->default_boot_option_priority = new_prio;
566
567         pb_log("handler: boot option %s set as default, timeout %u sec.\n",
568                opt->option->id, handler->sec_to_boot);
569
570         default_timeout(handler);
571 }
572
573 static bool resource_is_resolved(struct resource *res)
574 {
575         return !res || res->resolved;
576 }
577
578 /* We only use this in an assert, which will disappear if we're compiling
579  * with NDEBUG, so we need the 'used' attribute for these builds */
580 static bool __attribute__((used)) boot_option_is_resolved(
581                 struct discover_boot_option *opt)
582 {
583         return resource_is_resolved(opt->boot_image) &&
584                 resource_is_resolved(opt->initrd) &&
585                 resource_is_resolved(opt->dtb) &&
586                 resource_is_resolved(opt->icon);
587 }
588
589 static bool resource_resolve(struct resource *res, const char *name,
590                 struct discover_boot_option *opt,
591                 struct device_handler *handler)
592 {
593         struct parser *parser = opt->source;
594
595         if (resource_is_resolved(res))
596                 return true;
597
598         pb_debug("Attempting to resolve resource %s->%s with parser %s\n",
599                         opt->option->id, name, parser->name);
600         parser->resolve_resource(handler, res);
601
602         return res->resolved;
603 }
604
605 static bool boot_option_resolve(struct discover_boot_option *opt,
606                 struct device_handler *handler)
607 {
608         return resource_resolve(opt->boot_image, "boot_image", opt, handler) &&
609                 resource_resolve(opt->initrd, "initrd", opt, handler) &&
610                 resource_resolve(opt->dtb, "dtb", opt, handler) &&
611                 resource_resolve(opt->icon, "icon", opt, handler);
612 }
613
614 static void boot_option_finalise(struct device_handler *handler,
615                 struct discover_boot_option *opt)
616 {
617         assert(boot_option_is_resolved(opt));
618
619         /* check that the parsers haven't set any of the final data */
620         assert(!opt->option->boot_image_file);
621         assert(!opt->option->initrd_file);
622         assert(!opt->option->dtb_file);
623         assert(!opt->option->icon_file);
624         assert(!opt->option->device_id);
625
626         if (opt->boot_image)
627                 opt->option->boot_image_file = opt->boot_image->url->full;
628         if (opt->initrd)
629                 opt->option->initrd_file = opt->initrd->url->full;
630         if (opt->dtb)
631                 opt->option->dtb_file = opt->dtb->url->full;
632         if (opt->icon)
633                 opt->option->icon_file = opt->icon->url->full;
634
635         opt->option->device_id = opt->device->device->id;
636
637         if (opt->option->is_default)
638                 set_default(handler, opt);
639 }
640
641 static void notify_boot_option(struct device_handler *handler,
642                 struct discover_boot_option *opt)
643 {
644         struct discover_device *dev = opt->device;
645
646         if (!dev->notified)
647                 discover_server_notify_device_add(handler->server,
648                                                   opt->device->device);
649         dev->notified = true;
650         discover_server_notify_boot_option_add(handler->server, opt->option);
651 }
652
653 static void process_boot_option_queue(struct device_handler *handler)
654 {
655         struct discover_boot_option *opt, *tmp;
656
657         list_for_each_entry_safe(&handler->unresolved_boot_options,
658                         opt, tmp, list) {
659
660                 pb_debug("queue: attempting resolution for %s\n",
661                                 opt->option->id);
662
663                 if (!boot_option_resolve(opt, handler))
664                         continue;
665
666                 pb_debug("\tresolved!\n");
667
668                 list_remove(&opt->list);
669                 list_add_tail(&opt->device->boot_options, &opt->list);
670                 talloc_steal(opt->device, opt);
671                 boot_option_finalise(handler, opt);
672                 notify_boot_option(handler, opt);
673         }
674 }
675
676 struct discover_context *device_handler_discover_context_create(
677                 struct device_handler *handler,
678                 struct discover_device *device)
679 {
680         struct discover_context *ctx;
681
682         ctx = talloc_zero(handler, struct discover_context);
683         ctx->device = device;
684         ctx->network = handler->network;
685         list_init(&ctx->boot_options);
686
687         return ctx;
688 }
689
690 /**
691  * context_commit - Commit a temporary discovery context to the handler,
692  * and notify the clients about any new options / devices
693  */
694 void device_handler_discover_context_commit(struct device_handler *handler,
695                 struct discover_context *ctx)
696 {
697         struct discover_device *dev = ctx->device;
698         struct discover_boot_option *opt, *tmp;
699
700         if (!device_lookup_by_id(handler, dev->device->id))
701                 device_handler_add_device(handler, dev);
702
703         /* move boot options from the context to the device */
704         list_for_each_entry_safe(&ctx->boot_options, opt, tmp, list) {
705                 list_remove(&opt->list);
706
707                 if (boot_option_resolve(opt, handler)) {
708                         pb_log("boot option %s is resolved, "
709                                         "sending to clients\n",
710                                         opt->option->id);
711                         list_add_tail(&dev->boot_options, &opt->list);
712                         talloc_steal(dev, opt);
713                         boot_option_finalise(handler, opt);
714                         notify_boot_option(handler, opt);
715                 } else {
716                         if (!opt->source->resolve_resource) {
717                                 pb_log("parser %s gave us an unresolved "
718                                         "resource (%s), but no way to "
719                                         "resolve it\n",
720                                         opt->source->name, opt->option->id);
721                                 talloc_free(opt);
722                         } else {
723                                 pb_log("boot option %s is unresolved, "
724                                                 "adding to queue\n",
725                                                 opt->option->id);
726                                 list_add(&handler->unresolved_boot_options,
727                                                 &opt->list);
728                                 talloc_steal(handler, opt);
729                         }
730                 }
731         }
732 }
733
734 void device_handler_add_device(struct device_handler *handler,
735                 struct discover_device *device)
736 {
737         handler->n_devices++;
738         handler->devices = talloc_realloc(handler, handler->devices,
739                                 struct discover_device *, handler->n_devices);
740         handler->devices[handler->n_devices - 1] = device;
741
742         if (device->device->type == DEVICE_TYPE_NETWORK)
743                 network_register_device(handler->network, device);
744 }
745
746 /* Start discovery on a hotplugged device. The device will be in our devices
747  * array, but has only just been initialised by the hotplug source.
748  */
749 int device_handler_discover(struct device_handler *handler,
750                 struct discover_device *dev)
751 {
752         struct discover_context *ctx;
753         int rc;
754
755         process_boot_option_queue(handler);
756
757         /* create our context */
758         ctx = device_handler_discover_context_create(handler, dev);
759
760         rc = mount_device(dev);
761         if (rc)
762                 goto out;
763
764         /* add this device to our system info */
765         system_info_register_blockdev(dev->device->id, dev->uuid,
766                         dev->mount_path);
767
768         /* run the parsers. This will populate the ctx's boot_option list. */
769         iterate_parsers(ctx);
770
771         /* add discovered stuff to the handler */
772         device_handler_discover_context_commit(handler, ctx);
773
774 out:
775         talloc_free(ctx);
776
777         return 0;
778 }
779
780 /* Incoming dhcp event */
781 int device_handler_dhcp(struct device_handler *handler,
782                 struct discover_device *dev, struct event *event)
783 {
784         struct discover_context *ctx;
785
786         /* create our context */
787         ctx = device_handler_discover_context_create(handler, dev);
788         ctx->event = event;
789
790         iterate_parsers(ctx);
791
792         device_handler_discover_context_commit(handler, ctx);
793
794         talloc_free(ctx);
795
796         return 0;
797 }
798
799 /* incoming conf event */
800 int device_handler_conf(struct device_handler *handler,
801                 struct discover_device *dev, struct pb_url *url)
802 {
803         struct discover_context *ctx;
804
805         /* create our context */
806         ctx = device_handler_discover_context_create(handler, dev);
807         ctx->conf_url = url;
808
809         iterate_parsers(ctx);
810
811         device_handler_discover_context_commit(handler, ctx);
812
813         talloc_free(ctx);
814
815         return 0;
816 }
817
818 static struct discover_boot_option *find_boot_option_by_id(
819                 struct device_handler *handler, const char *id)
820 {
821         unsigned int i;
822
823         for (i = 0; i < handler->n_devices; i++) {
824                 struct discover_device *dev = handler->devices[i];
825                 struct discover_boot_option *opt;
826
827                 list_for_each_entry(&dev->boot_options, opt, list)
828                         if (!strcmp(opt->option->id, id))
829                                 return opt;
830         }
831
832         return NULL;
833 }
834
835 void device_handler_boot(struct device_handler *handler,
836                 struct boot_command *cmd)
837 {
838         struct discover_boot_option *opt = NULL;
839
840         if (cmd->option_id && strlen(cmd->option_id))
841                 opt = find_boot_option_by_id(handler, cmd->option_id);
842
843         if (handler->pending_boot)
844                 boot_cancel(handler->pending_boot);
845
846         platform_pre_boot();
847
848         handler->pending_boot = boot(handler, opt, cmd, handler->dry_run,
849                         boot_status, handler);
850         handler->pending_boot_is_default = false;
851 }
852
853 void device_handler_cancel_default(struct device_handler *handler)
854 {
855         struct boot_status status;
856
857         if (handler->timeout_waiter)
858                 waiter_remove(handler->timeout_waiter);
859
860         handler->timeout_waiter = NULL;
861         handler->autoboot_enabled = false;
862
863         /* we only send status if we had a default boot option queued */
864         if (!handler->default_boot_option)
865                 return;
866
867         pb_log("Cancelling default boot option\n");
868
869         if (handler->pending_boot && handler->pending_boot_is_default) {
870                 boot_cancel(handler->pending_boot);
871                 handler->pending_boot = NULL;
872                 handler->pending_boot_is_default = false;
873         }
874
875         handler->default_boot_option = NULL;
876
877         status.type = BOOT_STATUS_INFO;
878         status.progress = -1;
879         status.detail = NULL;
880         status.message = _("Default boot cancelled");
881
882         discover_server_notify_boot_status(handler->server, &status);
883 }
884
885 void device_handler_update_config(struct device_handler *handler,
886                 struct config *config)
887 {
888         int rc;
889
890         rc = config_set(config);
891         if (rc)
892                 return;
893
894         discover_server_notify_config(handler->server, config);
895         device_handler_update_lang(config->lang);
896         device_handler_reinit(handler);
897 }
898
899 static char *device_from_addr(void *ctx, struct pb_url *url)
900 {
901         char *ipaddr, *buf, *tok, *dev = NULL;
902         const char *delim = " ";
903         struct sockaddr_in *ip;
904         struct sockaddr_in si;
905         struct addrinfo *res;
906         struct process *p;
907         int rc;
908
909         /* Note: IPv4 only */
910         rc = inet_pton(AF_INET, url->host, &(si.sin_addr));
911         if (rc > 0) {
912                 ipaddr = url->host;
913         } else {
914                 /* need to turn hostname into a valid IP */
915                 rc = getaddrinfo(url->host, NULL, NULL, &res);
916                 if (rc) {
917                         pb_debug("%s: Invalid URL\n",__func__);
918                         return NULL;
919                 }
920                 ipaddr = talloc_array(ctx,char,INET_ADDRSTRLEN);
921                 ip = (struct sockaddr_in *) res->ai_addr;
922                 inet_ntop(AF_INET, &(ip->sin_addr), ipaddr, INET_ADDRSTRLEN);
923                 freeaddrinfo(res);
924         }
925
926         const char *argv[] = {
927                 pb_system_apps.ip,
928                 "route", "show", "to", "match",
929                 ipaddr,
930                 NULL
931         };
932
933         p = process_create(ctx);
934
935         p->path = pb_system_apps.ip;
936         p->argv = argv;
937         p->keep_stdout = true;
938
939         rc = process_run_sync(p);
940
941         if (rc) {
942                 /* ip has complained for some reason; most likely
943                  * there is no route to the host - bail out */
944                 pb_debug("%s: No route to %s\n",__func__,url->host);
945                 return NULL;
946         }
947
948         buf = p->stdout_buf;
949         /* If a route is found, ip-route output will be of the form
950          * "... dev DEVNAME ... " */
951         tok = strtok(buf, delim);
952         while (tok) {
953                 if (!strcmp(tok, "dev")) {
954                         tok = strtok(NULL, delim);
955                         dev = talloc_strdup(ctx, tok);
956                         break;
957                 }
958                 tok = strtok(NULL, delim);
959         }
960
961         process_release(p);
962         if (dev)
963                 pb_debug("%s: Found interface '%s'\n", __func__,dev);
964         return dev;
965 }
966
967
968 void device_handler_process_url(struct device_handler *handler,
969                 const char *url)
970 {
971         struct discover_context *ctx;
972         struct discover_device *dev;
973         struct boot_status *status;
974         struct pb_url *pb_url;
975         struct event *event;
976         struct param *param;
977
978         status = talloc(handler, struct boot_status);
979
980         status->type = BOOT_STATUS_ERROR;
981         status->progress = 0;
982         status->detail = talloc_asprintf(status,
983                         _("Received config URL %s"), url);
984
985         if (!handler->network) {
986                 status->message = talloc_asprintf(handler,
987                                         _("No network configured"));
988                 goto msg;
989         }
990
991         event = talloc(handler, struct event);
992         event->type = EVENT_TYPE_USER;
993         event->action = EVENT_ACTION_CONF;
994
995         event->params = talloc_array(event, struct param, 1);
996         param = &event->params[0];
997         param->name = talloc_strdup(event, "pxeconffile");
998         param->value = talloc_strdup(event, url);
999         event->n_params = 1;
1000
1001         pb_url = pb_url_parse(event, event->params->value);
1002         if (!pb_url || !pb_url->host) {
1003                 status->message = talloc_asprintf(handler,
1004                                         _("Invalid config URL!"));
1005                 goto msg;
1006         }
1007
1008         event->device = device_from_addr(event, pb_url);
1009         if (!event->device) {
1010                 status->message = talloc_asprintf(status,
1011                                         _("Unable to route to host %s"),
1012                                         pb_url->host);
1013                 goto msg;
1014         }
1015
1016         dev = discover_device_create(handler, event->device);
1017         ctx = device_handler_discover_context_create(handler, dev);
1018         ctx->event = event;
1019
1020         iterate_parsers(ctx);
1021
1022         device_handler_discover_context_commit(handler, ctx);
1023
1024         talloc_free(ctx);
1025
1026         status->type = BOOT_STATUS_INFO;
1027         status->message = talloc_asprintf(status, _("Config file %s parsed"),
1028                                         pb_url->file);
1029 msg:
1030         boot_status(handler, status);
1031         talloc_free(status);
1032 }
1033
1034 #ifndef PETITBOOT_TEST
1035
1036 static void device_handler_update_lang(const char *lang)
1037 {
1038         const char *cur_lang;
1039
1040         if (!lang)
1041                 return;
1042
1043         cur_lang = setlocale(LC_ALL, NULL);
1044         if (cur_lang && !strcmp(cur_lang, lang))
1045                 return;
1046
1047         setlocale(LC_ALL, lang);
1048 }
1049
1050 static int device_handler_init_sources(struct device_handler *handler)
1051 {
1052         /* init our device sources: udev, network and user events */
1053         handler->udev = udev_init(handler, handler->waitset);
1054         if (!handler->udev)
1055                 return -1;
1056
1057         handler->network = network_init(handler, handler->waitset,
1058                         handler->dry_run);
1059         if (!handler->network)
1060                 return -1;
1061
1062         handler->user_event = user_event_init(handler, handler->waitset);
1063         if (!handler->user_event)
1064                 return -1;
1065
1066         return 0;
1067 }
1068
1069 static void device_handler_reinit_sources(struct device_handler *handler)
1070 {
1071         /* if we haven't initialised sources previously (becuase we started in
1072          * safe mode), then init once here. */
1073         if (!(handler->udev || handler->network || handler->user_event)) {
1074                 device_handler_init_sources(handler);
1075                 return;
1076         }
1077
1078         udev_reinit(handler->udev);
1079
1080         network_shutdown(handler->network);
1081         handler->network = network_init(handler, handler->waitset,
1082                         handler->dry_run);
1083 }
1084
1085 static const char *fs_parameters(unsigned int rw_flags, const char *fstype)
1086 {
1087         if ((rw_flags | MS_RDONLY) != MS_RDONLY)
1088                 return "";
1089
1090         /* Avoid writing back to the disk on journaled filesystems */
1091         if (!strncmp(fstype, "ext4", strlen("ext4")))
1092                 return "norecovery";
1093         if (!strncmp(fstype, "xfs", strlen("xfs")))
1094                 return "norecovery";
1095
1096         return "";
1097 }
1098
1099 static bool check_existing_mount(struct discover_device *dev)
1100 {
1101         struct stat devstat, mntstat;
1102         struct mntent *mnt;
1103         FILE *fp;
1104         int rc;
1105
1106         rc = stat(dev->device_path, &devstat);
1107         if (rc) {
1108                 pb_debug("%s: stat failed: %s\n", __func__, strerror(errno));
1109                 return false;
1110         }
1111
1112         if (!S_ISBLK(devstat.st_mode)) {
1113                 pb_debug("%s: %s isn't a block device?\n", __func__,
1114                                 dev->device_path);
1115                 return false;
1116         }
1117
1118         fp = fopen("/proc/self/mounts", "r");
1119
1120         for (;;) {
1121                 mnt = getmntent(fp);
1122                 if (!mnt)
1123                         break;
1124
1125                 if (!mnt->mnt_fsname || mnt->mnt_fsname[0] != '/')
1126                         continue;
1127
1128                 rc = stat(mnt->mnt_fsname, &mntstat);
1129                 if (rc)
1130                         continue;
1131
1132                 if (!S_ISBLK(mntstat.st_mode))
1133                         continue;
1134
1135                 if (mntstat.st_rdev == devstat.st_rdev) {
1136                         dev->mount_path = talloc_strdup(dev, mnt->mnt_dir);
1137                         dev->mounted_rw = !!hasmntopt(mnt, "rw");
1138                         dev->mounted = true;
1139                         dev->unmount = false;
1140
1141                         pb_debug("%s: %s is already mounted (r%c) at %s\n",
1142                                         __func__, dev->device_path,
1143                                         dev->mounted_rw ? 'w' : 'o',
1144                                         mnt->mnt_dir);
1145                         break;
1146                 }
1147         }
1148
1149         fclose(fp);
1150
1151         return mnt != NULL;
1152 }
1153
1154 static int mount_device(struct discover_device *dev)
1155 {
1156         const char *fstype;
1157         int rc;
1158
1159         if (!dev->device_path)
1160                 return -1;
1161
1162         if (dev->mounted)
1163                 return 0;
1164
1165         if (check_existing_mount(dev))
1166                 return 0;
1167
1168         fstype = discover_device_get_param(dev, "ID_FS_TYPE");
1169         if (!fstype)
1170                 return 0;
1171
1172         /* ext3 treats the norecovery option as an error, so mount the device
1173          * as an ext4 filesystem instead */
1174         if (!strncmp(fstype, "ext3", strlen("ext3"))) {
1175                 pb_debug("Mounting ext3 filesystem as ext4\n");
1176                 fstype = talloc_asprintf(dev, "ext4");
1177         }
1178
1179         dev->mount_path = join_paths(dev, mount_base(),
1180                                         dev->device_path);
1181
1182         if (pb_mkdir_recursive(dev->mount_path)) {
1183                 pb_log("couldn't create mount directory %s: %s\n",
1184                                 dev->mount_path, strerror(errno));
1185                 goto err_free;
1186         }
1187
1188         pb_log("mounting device %s read-only\n", dev->device_path);
1189         errno = 0;
1190         rc = mount(dev->device_path, dev->mount_path, fstype,
1191                         MS_RDONLY | MS_SILENT,
1192                         fs_parameters(MS_RDONLY, fstype));
1193         if (!rc) {
1194                 dev->mounted = true;
1195                 dev->mounted_rw = false;
1196                 dev->unmount = true;
1197                 return 0;
1198         }
1199
1200         pb_log("couldn't mount device %s: mount failed: %s\n",
1201                         dev->device_path, strerror(errno));
1202
1203         pb_rmdir_recursive(mount_base(), dev->mount_path);
1204 err_free:
1205         talloc_free(dev->mount_path);
1206         dev->mount_path = NULL;
1207         return -1;
1208 }
1209
1210 static int umount_device(struct discover_device *dev)
1211 {
1212         int rc;
1213
1214         if (!dev->mounted || !dev->unmount)
1215                 return 0;
1216
1217         pb_log("unmounting device %s\n", dev->device_path);
1218         rc = umount(dev->mount_path);
1219         if (rc)
1220                 return -1;
1221
1222         dev->mounted = false;
1223
1224         pb_rmdir_recursive(mount_base(), dev->mount_path);
1225
1226         talloc_free(dev->mount_path);
1227         dev->mount_path = NULL;
1228
1229         return 0;
1230 }
1231
1232 int device_request_write(struct discover_device *dev, bool *release)
1233 {
1234         const char *fstype;
1235         int rc;
1236
1237         *release = false;
1238
1239         if (!dev->mounted)
1240                 return -1;
1241
1242         if (dev->mounted_rw)
1243                 return 0;
1244
1245         fstype = discover_device_get_param(dev, "ID_FS_TYPE");
1246
1247         pb_log("remounting device %s read-write\n", dev->device_path);
1248
1249         rc = umount(dev->mount_path);
1250         if (rc) {
1251                 pb_log("Failed to unmount %s\n", dev->mount_path);
1252                 return -1;
1253         }
1254         rc = mount(dev->device_path, dev->mount_path, fstype,
1255                         MS_SILENT,
1256                         fs_parameters(MS_REMOUNT, fstype));
1257         if (rc)
1258                 goto mount_ro;
1259
1260         dev->mounted_rw = true;
1261         *release = true;
1262         return 0;
1263
1264 mount_ro:
1265         pb_log("Unable to remount device %s read-write\n", dev->device_path);
1266         rc = mount(dev->device_path, dev->mount_path, fstype,
1267                         MS_RDONLY | MS_SILENT,
1268                         fs_parameters(MS_RDONLY, fstype));
1269         if (rc)
1270                 pb_log("Unable to recover mount for %s\n", dev->device_path);
1271         return -1;
1272 }
1273
1274 void device_release_write(struct discover_device *dev, bool release)
1275 {
1276         const char *fstype;
1277
1278         if (!release)
1279                 return;
1280
1281         fstype = discover_device_get_param(dev, "ID_FS_TYPE");
1282
1283         pb_log("remounting device %s read-only\n", dev->device_path);
1284         mount(dev->device_path, dev->mount_path, "",
1285                         MS_REMOUNT | MS_RDONLY | MS_SILENT,
1286                         fs_parameters(MS_RDONLY, fstype));
1287         dev->mounted_rw = false;
1288 }
1289
1290 #else
1291
1292 static void device_handler_update_lang(const char *lang __attribute__((unused)))
1293 {
1294 }
1295
1296 static int device_handler_init_sources(
1297                 struct device_handler *handler __attribute__((unused)))
1298 {
1299         return 0;
1300 }
1301
1302 static void device_handler_reinit_sources(
1303                 struct device_handler *handler __attribute__((unused)))
1304 {
1305 }
1306
1307 static int umount_device(struct discover_device *dev __attribute__((unused)))
1308 {
1309         return 0;
1310 }
1311
1312 static int __attribute__((unused)) mount_device(
1313                 struct discover_device *dev __attribute__((unused)))
1314 {
1315         return 0;
1316 }
1317
1318 int device_request_write(struct discover_device *dev __attribute__((unused)),
1319                 bool *release)
1320 {
1321         *release = true;
1322         return 0;
1323 }
1324
1325 void device_release_write(struct discover_device *dev __attribute__((unused)),
1326         bool release __attribute__((unused)))
1327 {
1328 }
1329
1330 #endif
1331