]> git.ozlabs.org Git - petitboot/blob - discover/platform.c
Various fixups and checks to make scan-build happy
[petitboot] / discover / platform.c
1
2 #define _GNU_SOURCE
3
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <locale.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include <log/log.h>
11 #include <file/file.h>
12 #include <types/types.h>
13 #include <talloc/talloc.h>
14 #include <url/url.h>
15
16 #include "platform.h"
17
18 void                    *platform_ctx;
19 static struct platform  *platform;
20 static struct config    *config;
21
22 static const char *kernel_cmdline_debug = "petitboot.debug";
23
24 static void dump_config(struct config *config)
25 {
26         unsigned int i;
27
28         pb_log("configuration:\n");
29
30         if (config->autoboot_enabled)
31                 pb_log(" autoboot: enabled, %d sec\n",
32                                 config->autoboot_timeout_sec);
33         else
34                 pb_log(" autoboot: disabled\n");
35
36         if (config->network.n_interfaces || config->network.n_dns_servers)
37                 pb_log(" network configuration:\n");
38
39         if (config->safe_mode)
40                 pb_log(" safe mode: active\n");
41
42         if (config->disable_snapshots)
43                 pb_log(" dm-snapshots disabled\n");
44
45         for (i = 0; i < config->network.n_interfaces; i++) {
46                 struct interface_config *ifconf =
47                         config->network.interfaces[i];
48
49                 pb_log("  interface %02x:%02x:%02x:%02x:%02x:%02x\n",
50                                 ifconf->hwaddr[0], ifconf->hwaddr[1],
51                                 ifconf->hwaddr[2], ifconf->hwaddr[3],
52                                 ifconf->hwaddr[4], ifconf->hwaddr[5]);
53
54                 if (ifconf->ignore) {
55                         pb_log("   ignore\n");
56                         continue;
57                 }
58
59                 if (ifconf->method == CONFIG_METHOD_DHCP) {
60                         pb_log("   dhcp\n");
61
62                 } else if (ifconf->method == CONFIG_METHOD_STATIC) {
63                         pb_log("   static:\n");
64                         pb_log("    ip:  %s\n", ifconf->static_config.address);
65                         pb_log("    gw:  %s\n", ifconf->static_config.gateway);
66                         pb_log("    url:  %s\n", ifconf->static_config.url);
67
68                 }
69         }
70         for (i = 0; i < config->network.n_dns_servers; i++)
71                 pb_log("  dns server %s\n", config->network.dns_servers[i]);
72
73         for (i = 0; i < config->n_autoboot_opts; i++) {
74                 if (config->autoboot_opts[i].boot_type == BOOT_DEVICE_TYPE)
75                         pb_log("  boot device %d: %s\n", i,
76                                device_type_name(config->autoboot_opts[i].type));
77                 else
78                         pb_log("  boot device %d: uuid: %s\n",
79                                i, config->autoboot_opts[i].uuid);
80         }
81
82         pb_log("  IPMI boot device 0x%02x%s\n", config->ipmi_bootdev,
83                         config->ipmi_bootdev_persistent ? " (persistent)" : "");
84
85         pb_log("  Modifications allowed to disks: %s\n",
86                         config->allow_writes ? "yes" : "no");
87
88         pb_log("  Default UI to boot on: %s\n",
89                 config->boot_console ?: "none set");
90         if (config->manual_console)
91                 pb_log("    (Manually set)\n");
92
93         if (config->http_proxy)
94                 pb_log("  HTTP Proxy: %s\n", config->http_proxy);
95         if (config->https_proxy)
96                 pb_log("  HTTPS Proxy: %s\n", config->https_proxy);
97
98
99         pb_log(" language: %s\n", config->lang ?: "");
100 }
101
102 static bool config_debug_on_cmdline(void)
103 {
104         char buf[600];
105         int rc, fd;
106
107         fd = open("/proc/cmdline", O_RDONLY);
108         if (fd < 0)
109                 return false;
110
111         rc = read(fd, buf, sizeof(buf));
112         close(fd);
113
114         if (rc <= 0)
115                 return false;
116
117         return memmem(buf, rc, kernel_cmdline_debug,
118                         strlen(kernel_cmdline_debug)) != NULL;
119 }
120
121 void config_set_defaults(struct config *config)
122 {
123         const char *lang;
124
125         config->autoboot_enabled = true;
126         config->autoboot_timeout_sec = 10;
127         config->autoboot_enabled = true;
128         config->network.interfaces = NULL;
129         config->network.n_interfaces = 0;
130         config->network.dns_servers = NULL;
131         config->network.n_dns_servers = 0;
132         config->http_proxy = NULL;
133         config->https_proxy = NULL;
134         config->safe_mode = false;
135         config->allow_writes = true;
136         config->disable_snapshots = false;
137
138         config->n_consoles = 0;
139         config->consoles = NULL;
140         config->boot_console = NULL;
141
142         config->n_autoboot_opts = 2;
143         config->autoboot_opts = talloc_array(config, struct autoboot_option,
144                                                 config->n_autoboot_opts);
145         config->autoboot_opts[0].boot_type = BOOT_DEVICE_TYPE;
146         config->autoboot_opts[0].type = DEVICE_TYPE_NETWORK;
147         config->autoboot_opts[1].boot_type = BOOT_DEVICE_TYPE;
148         config->autoboot_opts[1].type = DEVICE_TYPE_ANY;
149
150         config->ipmi_bootdev = 0;
151         config->ipmi_bootdev_persistent = false;
152
153         config->debug = config_debug_on_cmdline();
154
155         lang = setlocale(LC_ALL, NULL);
156         pb_log_fn("lang: %s\n", lang);
157         if (lang && strlen(lang))
158                 config->lang = talloc_strdup(config, lang);
159         else
160                 config->lang = NULL;
161
162 }
163
164 int platform_init(void *ctx)
165 {
166         extern struct platform *__start_platforms,  *__stop_platforms;
167         struct platform **p;
168
169         platform_ctx = talloc_new(ctx);
170
171         for (p = &__start_platforms; p < &__stop_platforms; p++) {
172                 pb_debug("%s: Try platform %s\n", __func__, (*p)->name);
173                 if (!(*p)->probe(*p, platform_ctx))
174                         continue;
175                 platform = *p;
176                 break;
177         }
178
179         config = talloc(platform_ctx, struct config);
180         config_set_defaults(config);
181
182         if (platform) {
183                 pb_log("Detected platform type: %s\n", platform->name);
184                 if (platform->load_config)
185                         platform->load_config(platform, config);
186         } else {
187                 pb_log("No platform type detected, some platform-specific "
188                                 "functionality will be disabled\n");
189         }
190
191         dump_config(config);
192
193         return 0;
194 }
195
196 const struct platform *platform_get(void)
197 {
198         return platform;
199 }
200
201 void platform_pre_boot(void)
202 {
203         const struct config *config = config_get();
204
205         if (platform && config && platform->pre_boot)
206                 platform->pre_boot(platform, config);
207 }
208
209 int platform_get_sysinfo(struct system_info *info)
210 {
211         if (platform && platform->get_sysinfo)
212                 return platform->get_sysinfo(platform, info);
213         return -1;
214 }
215
216 bool platform_restrict_clients(){
217         if (platform && platform->restrict_clients)
218                 return platform->restrict_clients(platform);
219         return false;
220 }
221
222 int platform_set_password(const char *hash)
223 {
224         if (platform && platform->set_password)
225                 return platform->set_password(platform, hash);
226         return -1;
227 }
228
229 int config_set(struct config *newconfig)
230 {
231         int rc;
232
233         if (!platform || !platform->save_config)
234                 return -1;
235
236         if (newconfig == config)
237                 return 0;
238
239         pb_log("new configuration data received\n");
240         dump_config(newconfig);
241
242         rc = platform->save_config(platform, newconfig);
243
244         if (!rc)
245                 config = talloc_steal(platform_ctx, newconfig);
246         else
247                 pb_log("error saving new configuration; changes lost\n");
248
249         return rc;
250 }
251
252 /* A non-exported function to allow the test infrastructure to initialise
253  * (and change) the configuration variables */
254 struct parser_test;
255 struct config __attribute__((unused)) *test_config_init(
256                 struct parser_test *test);
257 struct config *test_config_init(struct parser_test *test)
258 {
259         config = talloc(test, struct config);
260         config_set_defaults(config);
261         return config;
262 }
263
264 const struct config *config_get(void)
265 {
266         return config;
267 }
268
269 void config_set_autoboot(bool autoboot_enabled)
270 {
271         config->autoboot_enabled = autoboot_enabled;
272
273         pb_log("set autoboot: %s\n",
274                         config->autoboot_enabled ? "enabled" : "disabled");
275 }
276
277 int platform_fini(void)
278 {
279         talloc_free(platform_ctx);
280         return 0;
281 }
282
283 static int parse_hwaddr(struct interface_config *ifconf, const char *str)
284 {
285         int i;
286
287         if (strlen(str) != strlen("00:00:00:00:00:00"))
288                 return -1;
289
290         for (i = 0; i < HWADDR_SIZE; i++) {
291                 char byte[3], *endp;
292                 unsigned long x;
293
294                 byte[0] = str[i * 3 + 0];
295                 byte[1] = str[i * 3 + 1];
296                 byte[2] = '\0';
297
298                 x = strtoul(byte, &endp, 16);
299                 if (endp != byte + 2)
300                         return -1;
301
302                 ifconf->hwaddr[i] = x & 0xff;
303         }
304
305         return 0;
306 }
307
308 static int config_parse_one_interface(struct config *config, char *confstr)
309 {
310         struct interface_config *ifconf;
311         char *tok, *tok_gw, *tok_url, *saveptr;
312
313         ifconf = talloc_zero(config, struct interface_config);
314
315         if (!confstr || !strlen(confstr))
316                 goto out_err;
317
318         /* first token should be the mac address */
319         tok = strtok_r(confstr, ",", &saveptr);
320         if (!tok)
321                 goto out_err;
322
323         if (parse_hwaddr(ifconf, tok))
324                 goto out_err;
325
326         /* second token is the method */
327         tok = strtok_r(NULL, ",", &saveptr);
328         if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
329                 ifconf->ignore = true;
330
331         } else if (!strcmp(tok, "dhcp")) {
332                 ifconf->method = CONFIG_METHOD_DHCP;
333
334         } else if (!strcmp(tok, "static")) {
335                 ifconf->method = CONFIG_METHOD_STATIC;
336
337                 /* ip/mask, [optional] gateway, [optional] url */
338                 tok = strtok_r(NULL, ",", &saveptr);
339                 if (!tok)
340                         goto out_err;
341                 ifconf->static_config.address =
342                         talloc_strdup(ifconf, tok);
343
344                 /*
345                  * If a url is set but not a gateway, we can accidentally
346                  * interpret the url as the gateway. To avoid changing the
347                  * parameter format check if the "gateway" is actually a
348                  * pb-url if it's the last token.
349                  */
350                 tok_gw = strtok_r(NULL, ",", &saveptr);
351                 tok_url = strtok_r(NULL, ",", &saveptr);
352
353                 if (tok_gw) {
354                         if (tok_url || !is_url(tok_gw))
355                                 ifconf->static_config.gateway =
356                                         talloc_strdup(ifconf, tok_gw);
357                         else
358                                         tok_url = tok_gw;
359                 }
360
361                 if (tok_url)
362                         ifconf->static_config.url =
363                                 talloc_strdup(ifconf, tok_url);
364         } else {
365                 pb_log("Unknown network configuration method %s\n", tok);
366                 goto out_err;
367         }
368
369         config->network.interfaces = talloc_realloc(config,
370                         config->network.interfaces,
371                         struct interface_config *,
372                         ++config->network.n_interfaces);
373
374         config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
375
376         return 0;
377 out_err:
378         talloc_free(ifconf);
379         return -1;
380 }
381
382 static int config_parse_one_dns(struct config *config, char *confstr)
383 {
384         char *tok, *saveptr = NULL;
385
386         for (tok = strtok_r(confstr, ",", &saveptr); tok;
387                         tok = strtok_r(NULL, ",", &saveptr)) {
388
389                 char *server = talloc_strdup(config, tok);
390
391                 config->network.dns_servers = talloc_realloc(config,
392                                 config->network.dns_servers, const char *,
393                                 ++config->network.n_dns_servers);
394
395                 config->network.dns_servers[config->network.n_dns_servers - 1]
396                                 = server;
397         }
398
399         return 0;
400 }
401
402 static void config_populate_network(struct config *config, const char *cval)
403 {
404         char *val, *saveptr = NULL;
405         int i;
406
407         if (!cval || !strlen(cval))
408                 return;
409
410         val = talloc_strdup(config, cval);
411
412         for (i = 0; ; i++) {
413                 char *tok;
414
415                 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
416                 if (!tok)
417                         break;
418
419                 if (!strncasecmp(tok, "dns,", strlen("dns,")))
420                         config_parse_one_dns(config, tok + strlen("dns,"));
421                 else
422                         config_parse_one_interface(config, tok);
423
424         }
425
426         talloc_free(val);
427 }
428
429 static int read_bootdev(void *ctx, char **pos, struct autoboot_option *opt)
430 {
431         char *delim = strchr(*pos, ' ');
432         int len, prefix = 0, rc = -1;
433         enum device_type type;
434
435         if (!strncmp(*pos, "uuid:", strlen("uuid:"))) {
436                 prefix = strlen("uuid:");
437                 opt->boot_type = BOOT_DEVICE_UUID;
438         } else if (!strncmp(*pos, "mac:", strlen("mac:"))) {
439                 prefix = strlen("mac:");
440                 opt->boot_type = BOOT_DEVICE_UUID;
441         } else {
442                 type = find_device_type(*pos);
443                 if (type != DEVICE_TYPE_UNKNOWN) {
444                         opt->type = type;
445                         opt->boot_type = BOOT_DEVICE_TYPE;
446                         rc = 0;
447                 }
448         }
449
450         if (opt->boot_type == BOOT_DEVICE_UUID) {
451                 if (delim)
452                         len = (int)(delim - *pos) - prefix;
453                 else
454                         len = strlen(*pos) - prefix;
455
456                 if (len) {
457                         opt->uuid = talloc_strndup(ctx, *pos + prefix, len);
458                         rc = 0;
459                 }
460         }
461
462         /* Always advance pointer to next option or end */
463         if (delim)
464                 *pos = delim + 1;
465         else
466                 *pos += strlen(*pos);
467
468         return rc;
469 }
470
471 void config_populate_bootdev(struct config *config,
472         const struct param_list *pl)
473 {
474         struct autoboot_option *opt, *new = NULL;
475         char *pos, *end;
476         unsigned int n_new = 0;
477         const char *val;
478
479         /* Check for ordered bootdevs */
480         val = param_list_get_value(pl, "petitboot,bootdevs");
481         if (!val || !strlen(val)) {
482                 pos = end = NULL;
483         } else {
484                 pos = talloc_strdup(config, val);
485                 end = strchr(pos, '\0');
486         }
487
488         while (pos && pos < end) {
489                 opt = talloc(config, struct autoboot_option);
490
491                 if (read_bootdev(config, &pos, opt)) {
492                         pb_log("bootdev config is in an unknown format "
493                                "(expected uuid:... or mac:...)\n");
494                         talloc_free(opt);
495                         continue;
496                 }
497
498                 new = talloc_realloc(config, new, struct autoboot_option,
499                                      n_new + 1);
500                 new[n_new] = *opt;
501                 n_new++;
502                 talloc_free(opt);
503
504         }
505
506         if (!n_new) {
507                 /* If autoboot has been disabled, clear the default options */
508                 if (!config->autoboot_enabled) {
509                         talloc_free(config->autoboot_opts);
510                         config->n_autoboot_opts = 0;
511                 }
512                 return;
513         }
514
515         talloc_free(config->autoboot_opts);
516         config->autoboot_opts = new;
517         config->n_autoboot_opts = n_new;
518 }
519
520 void config_populate_all(struct config *config, const struct param_list *pl)
521 {
522         const char *val;
523         char *end;
524         unsigned long timeout;
525
526         /* if the "auto-boot?' property is present and "false", disable auto
527          * boot */
528         val = param_list_get_value(pl, "auto-boot?");
529         config->autoboot_enabled = !val || strcmp(val, "false");
530
531         val = param_list_get_value(pl, "petitboot,timeout");
532         if (val) {
533                 timeout = strtoul(val, &end, 10);
534                 if (end != val) {
535                         if (timeout >= INT_MAX)
536                                 timeout = INT_MAX;
537                         config->autoboot_timeout_sec = (int)timeout;
538                 }
539         }
540
541         val = param_list_get_value(pl, "petitboot,language");
542         config->lang = val ? talloc_strdup(config, val) : NULL;
543
544         val = param_list_get_value(pl, "petitboot,network");
545         config_populate_network(config, val);
546
547         config_populate_bootdev(config, pl);
548
549         if (!config->debug) {
550                 val = param_list_get_value(pl, "petitboot,debug?");
551                 config->debug = val && !strcmp(val, "true");
552         }
553
554         val = param_list_get_value(pl, "petitboot,write?");
555         if (val)
556                 config->allow_writes = !strcmp(val, "true");
557
558         val = param_list_get_value(pl, "petitboot,snapshots?");
559         if (val)
560                 config->disable_snapshots = !strcmp(val, "false");
561
562         val = param_list_get_value(pl, "petitboot,console");
563         if (val)
564                 config->boot_console = talloc_strdup(config, val);
565         /* If a full path is already set we don't want to override it */
566         config->manual_console = config->boot_console &&
567                                         !strchr(config->boot_console, '[');
568
569         val = param_list_get_value(pl, "petitboot,http_proxy");
570         if (val)
571                 config->http_proxy = talloc_strdup(config, val);
572         val = param_list_get_value(pl, "petitboot,https_proxy");
573         if (val)
574                 config->https_proxy = talloc_strdup(config, val);
575 }
576
577 static char *interface_config_str(void *ctx, struct interface_config *config)
578 {
579         char *str;
580
581         /* todo: HWADDR size is hardcoded as 6, but we may need to handle
582          * different hardware address formats */
583         str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
584                         config->hwaddr[0], config->hwaddr[1],
585                         config->hwaddr[2], config->hwaddr[3],
586                         config->hwaddr[4], config->hwaddr[5]);
587
588         if (config->ignore) {
589                 str = talloc_asprintf_append(str, "ignore");
590
591         } else if (config->method == CONFIG_METHOD_DHCP) {
592                 str = talloc_asprintf_append(str, "dhcp");
593
594         } else if (config->method == CONFIG_METHOD_STATIC) {
595                 str = talloc_asprintf_append(str, "static,%s%s%s%s%s",
596                                 config->static_config.address,
597                                 config->static_config.gateway ? "," : "",
598                                 config->static_config.gateway ?: "",
599                                 config->static_config.url ? "," : "",
600                                 config->static_config.url ?: "");
601         }
602         return str;
603 }
604
605 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
606 {
607         char *str;
608         int i;
609
610         str = talloc_strdup(ctx, "dns,");
611         for (i = 0; i < n; i++) {
612                 str = talloc_asprintf_append(str, "%s%s",
613                                 i == 0 ? "" : ",",
614                                 dns_servers[i]);
615         }
616
617         return str;
618 }
619
620 void params_update_network_values(struct param_list *pl,
621         const char *param_name, const struct config *config)
622 {
623         unsigned int i;
624         char *val;
625
626         /*
627          * Don't store IPMI overrides to NVRAM. If this was a persistent
628          * override it was already stored in NVRAM by
629          * get_ipmi_network_override()
630          */
631         if (config->network.n_interfaces &&
632                 config->network.interfaces[0]->override)
633                 return;
634
635         val = talloc_strdup(pl, "");
636
637         for (i = 0; i < config->network.n_interfaces; i++) {
638                 char *iface_str = interface_config_str(pl,
639                                         config->network.interfaces[i]);
640                 val = talloc_asprintf_append(val, "%s%s",
641                                 *val == '\0' ? "" : " ", iface_str);
642                 talloc_free(iface_str);
643         }
644
645         if (config->network.n_dns_servers) {
646                 char *dns_str = dns_config_str(pl,
647                                                 config->network.dns_servers,
648                                                 config->network.n_dns_servers);
649                 val = talloc_asprintf_append(val, "%s%s",
650                                 *val == '\0' ? "" : " ", dns_str);
651                 talloc_free(dns_str);
652         }
653
654         param_list_set_non_empty(pl, param_name, val, true);
655
656         talloc_free(val);
657 }
658
659 void params_update_bootdev_values(struct param_list *pl,
660         const char *param_name, const struct config *config)
661 {
662         char *val = NULL, *boot_str = NULL, *tmp = NULL;
663         struct autoboot_option *opt;
664         const char delim = ' ';
665         unsigned int i;
666
667         if (!config->n_autoboot_opts)
668                 val = "";
669
670         for (i = 0; i < config->n_autoboot_opts; i++) {
671                 opt = &config->autoboot_opts[i];
672                 switch (opt->boot_type) {
673                         case BOOT_DEVICE_TYPE:
674                                 boot_str = talloc_asprintf(config, "%s%c",
675                                                 device_type_name(opt->type),
676                                                 delim);
677                                 break;
678                         case BOOT_DEVICE_UUID:
679                                 boot_str = talloc_asprintf(config, "uuid:%s%c",
680                                                 opt->uuid, delim);
681                                 break;
682                         }
683                         tmp = val = talloc_asprintf_append(val, "%s", boot_str);
684         }
685
686         param_list_set_non_empty(pl, param_name, val, true);
687
688         talloc_free(tmp);
689         if (boot_str)
690                 talloc_free(boot_str);
691 }
692