pb-config: Move config storage modules to "platform" modules in discover code
[petitboot] / discover / platform-powerpc.c
1
2 #include <string.h>
3 #include <stdlib.h>
4 #include <limits.h>
5 #include <sys/types.h>
6 #include <sys/wait.h>
7
8 #include <talloc/talloc.h>
9 #include <list/list.h>
10 #include <log/log.h>
11 #include <process/process.h>
12
13 #include "platform.h"
14
15 static const char *partition = "common";
16
17 struct param {
18         char                    *name;
19         char                    *value;
20         bool                    modified;
21         struct list_item        list;
22 };
23
24 struct platform_powerpc {
25         struct list             params;
26 };
27
28 static const char *known_params[] = {
29         "auto-boot?",
30         "petitboot,network",
31         "petitboot,timeout",
32         NULL,
33 };
34
35 #define to_platform_powerpc(p) \
36         (struct platform_powerpc *)(p->platform_data)
37
38 /* a partition max a max size of 64k * 16bytes = 1M */
39 static const int max_partition_size = 64 * 1024 * 16;
40
41 static bool param_is_known(const char *param, unsigned int len)
42 {
43         const char *known_param;
44         unsigned int i;
45
46         for (i = 0; known_params[i]; i++) {
47                 known_param = known_params[i];
48                 if (len == strlen(known_param) &&
49                                 !strncmp(param, known_param, len))
50                         return true;
51         }
52
53         return false;
54 }
55
56 static int parse_nvram_params(struct platform_powerpc *platform,
57                 char *buf, int len)
58 {
59         char *pos, *name, *value;
60         unsigned int paramlen;
61         int i, count;
62
63         /* discard 2 header lines:
64          * "common" partiton"
65          * ------------------
66          */
67         pos = buf;
68         count = 0;
69
70         for (i = 0; i < len; i++) {
71                 if (pos[i] == '\n')
72                         count++;
73                 if (count == 2)
74                         break;
75         }
76
77         if (i == len) {
78                 fprintf(stderr, "failure parsing nvram output\n");
79                 return -1;
80         }
81
82         for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
83                 unsigned int namelen;
84                 struct param *param;
85                 char *newline;
86
87                 newline = strchr(pos, '\n');
88                 if (!newline)
89                         break;
90
91                 *newline = '\0';
92
93                 paramlen = strlen(pos);
94
95                 name = pos;
96                 value = strchr(pos, '=');
97                 if (!value)
98                         continue;
99
100                 namelen = value - name;
101                 if (namelen == 0)
102                         continue;
103
104                 if (!param_is_known(name, namelen))
105                         continue;
106
107                 value++;
108
109                 param = talloc(platform, struct param);
110                 param->modified = false;
111                 param->name = talloc_strndup(platform, name, namelen);
112                 param->value = talloc_strdup(platform, value);
113                 list_add(&platform->params, &param->list);
114         }
115
116         return 0;
117 }
118
119 static int parse_nvram(struct platform_powerpc *platform)
120 {
121         struct process *process;
122         const char *argv[5];
123         int rc;
124
125         argv[0] = "nvram";
126         argv[1] = "--print-config";
127         argv[2] = "--partition";
128         argv[3] = partition;
129         argv[4] = NULL;
130
131         process = process_create(platform);
132         process->path = "nvram";
133         process->argv = argv;
134         process->keep_stdout = true;
135
136         rc = process_run_sync(process);
137
138         if (rc || !process_exit_ok(process)) {
139                 fprintf(stderr, "nvram process returned "
140                                 "non-zero exit status\n");
141                 rc = -1;
142         } else {
143                 rc = parse_nvram_params(platform, process->stdout_buf,
144                                             process->stdout_len);
145         }
146
147         process_release(process);
148         return rc;
149 }
150
151 static int write_nvram(struct platform_powerpc *platform)
152 {
153         struct process *process;
154         struct param *param;
155         const char *argv[6];
156         int rc;
157
158         argv[0] = "nvram";
159         argv[1] = "--update-config";
160         argv[2] = NULL;
161         argv[3] = "--partition";
162         argv[4] = partition;
163         argv[5] = NULL;
164
165         process = process_create(platform);
166         process->path = "nvram";
167         process->argv = argv;
168
169         list_for_each_entry(&platform->params, param, list) {
170                 char *paramstr;
171
172                 if (!param->modified)
173                         continue;
174
175                 paramstr = talloc_asprintf(platform, "%s=%s",
176                                 param->name, param->value);
177                 argv[2] = paramstr;
178
179                 rc = process_run_sync(process);
180
181                 talloc_free(paramstr);
182
183                 if (rc || !process_exit_ok(process)) {
184                         rc = -1;
185                         pb_log("nvram update process returned "
186                                         "non-zero exit status\n");
187                         break;
188                 }
189         }
190
191         process_release(process);
192         return rc;
193 }
194
195 static const char *get_param(struct platform_powerpc *platform,
196                 const char *name)
197 {
198         struct param *param;
199
200         list_for_each_entry(&platform->params, param, list)
201                 if (!strcmp(param->name, name))
202                         return param->value;
203         return NULL;
204 }
205
206 static void set_param(struct platform_powerpc *platform, const char *name,
207                 const char *value)
208 {
209         struct param *param;
210
211         list_for_each_entry(&platform->params, param, list) {
212                 if (strcmp(param->name, name))
213                         continue;
214
215                 if (!strcmp(param->value, value))
216                         return;
217
218                 talloc_free(param->value);
219                 param->value = talloc_strdup(param, value);
220                 param->modified = true;
221                 return;
222         }
223
224
225         param = talloc(platform, struct param);
226         param->modified = true;
227         param->name = talloc_strdup(platform, name);
228         param->value = talloc_strdup(platform, value);
229         list_add(&platform->params, &param->list);
230 }
231
232 static int parse_hwaddr(struct interface_config *ifconf, char *str)
233 {
234         int i;
235
236         if (strlen(str) != strlen("00:00:00:00:00:00"))
237                 return -1;
238
239         for (i = 0; i < HWADDR_SIZE; i++) {
240                 char byte[3], *endp;
241                 unsigned long x;
242
243                 byte[0] = str[i * 3 + 0];
244                 byte[1] = str[i * 3 + 1];
245                 byte[2] = '\0';
246
247                 x = strtoul(byte, &endp, 16);
248                 if (endp != byte + 2)
249                         return -1;
250
251                 ifconf->hwaddr[i] = x & 0xff;
252         }
253
254         return 0;
255 }
256
257 static int parse_one_interface_config(struct config *config,
258                 char *confstr)
259 {
260         struct interface_config *ifconf;
261         char *tok, *saveptr;
262
263         ifconf = talloc_zero(config, struct interface_config);
264
265         if (!confstr || !strlen(confstr))
266                 goto out_err;
267
268         /* first token should be the mac address */
269         tok = strtok_r(confstr, ",", &saveptr);
270         if (!tok)
271                 goto out_err;
272
273         if (parse_hwaddr(ifconf, tok))
274                 goto out_err;
275
276         /* second token is the method */
277         tok = strtok_r(NULL, ",", &saveptr);
278         if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
279                 ifconf->ignore = true;
280
281         } else if (!strcmp(tok, "dhcp")) {
282                 ifconf->method = CONFIG_METHOD_DHCP;
283
284         } else if (!strcmp(tok, "static")) {
285                 ifconf->method = CONFIG_METHOD_STATIC;
286
287                 /* ip/mask, [optional] gateway */
288                 tok = strtok_r(NULL, ",", &saveptr);
289                 if (!tok)
290                         goto out_err;
291                 ifconf->static_config.address =
292                         talloc_strdup(ifconf, tok);
293
294                 tok = strtok_r(NULL, ",", &saveptr);
295                 if (tok) {
296                         ifconf->static_config.gateway =
297                                 talloc_strdup(ifconf, tok);
298                 }
299
300         } else {
301                 pb_log("Unknown network configuration method %s\n", tok);
302                 goto out_err;
303         }
304
305         config->network.interfaces = talloc_realloc(config,
306                         config->network.interfaces,
307                         struct interface_config *,
308                         ++config->network.n_interfaces);
309
310         config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
311
312         return 0;
313 out_err:
314         talloc_free(ifconf);
315         return -1;
316 }
317
318 static int parse_one_dns_config(struct config *config,
319                 char *confstr)
320 {
321         char *tok, *saveptr;
322
323         for (tok = strtok_r(confstr, ",", &saveptr); tok;
324                         tok = strtok_r(NULL, ",", &saveptr)) {
325
326                 char *server = talloc_strdup(config, tok);
327
328                 config->network.dns_servers = talloc_realloc(config,
329                                 config->network.dns_servers, const char *,
330                                 ++config->network.n_dns_servers);
331
332                 config->network.dns_servers[config->network.n_dns_servers - 1]
333                                 = server;
334         }
335
336         return 0;
337 }
338
339 static void populate_network_config(struct platform_powerpc *platform,
340                 struct config *config)
341 {
342         const char *cval;
343         char *val;
344         int i;
345
346         cval = get_param(platform, "petitboot,network");
347         if (!cval || !strlen(cval))
348                 return;
349
350         val = talloc_strdup(config, cval);
351
352         for (i = 0; ; i++) {
353                 char *tok, *saveptr;
354
355                 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
356                 if (!tok)
357                         break;
358
359                 if (!strncasecmp(tok, "dns,", strlen("dns,")))
360                         parse_one_dns_config(config, tok + strlen("dns,"));
361                 else
362                         parse_one_interface_config(config, tok);
363
364         }
365
366         talloc_free(val);
367 }
368
369 static void populate_config(struct platform_powerpc *platform,
370                 struct config *config)
371 {
372         const char *val;
373         char *end;
374         unsigned long timeout;
375
376         /* if the "auto-boot?' property is present and "false", disable auto
377          * boot */
378         val = get_param(platform, "auto-boot?");
379         config->autoboot_enabled = !val || strcmp(val, "false");
380
381         val = get_param(platform, "petitboot,timeout");
382         if (val) {
383                 timeout = strtoul(val, &end, 10);
384                 if (end != val) {
385                         if (timeout >= INT_MAX)
386                                 timeout = INT_MAX;
387                         config->autoboot_timeout_sec = (int)timeout;
388                 }
389         }
390
391         populate_network_config(platform, config);
392 }
393
394 static char *iface_config_str(void *ctx, struct interface_config *config)
395 {
396         char *str;
397
398         /* todo: HWADDR size is hardcoded as 6, but we may need to handle
399          * different hardware address formats */
400         str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
401                         config->hwaddr[0], config->hwaddr[1],
402                         config->hwaddr[2], config->hwaddr[3],
403                         config->hwaddr[4], config->hwaddr[5]);
404
405         if (config->ignore) {
406                 str = talloc_asprintf_append(str, "ignore");
407
408         } else if (config->method == CONFIG_METHOD_DHCP) {
409                 str = talloc_asprintf_append(str, "dhcp");
410
411         } else if (config->method == CONFIG_METHOD_STATIC) {
412                 str = talloc_asprintf_append(str, "static,%s%s%s",
413                                 config->static_config.address,
414                                 config->static_config.gateway ? "," : "",
415                                 config->static_config.gateway ?: "");
416         }
417         return str;
418 }
419
420 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
421 {
422         char *str;
423         int i;
424
425         str = talloc_strdup(ctx, "dns,");
426         for (i = 0; i < n; i++) {
427                 str = talloc_asprintf_append(str, "%s%s",
428                                 i == 0 ? "" : ",",
429                                 dns_servers[i]);
430         }
431
432         return str;
433 }
434
435 static void update_string_config(struct platform_powerpc *platform,
436                 const char *name, const char *value)
437 {
438         const char *cur;
439
440         cur = get_param(platform, name);
441
442         /* don't set an empty parameter if it doesn't already exist */
443         if (!cur && !strlen(value))
444                 return;
445
446         set_param(platform, name, value);
447 }
448
449 static void update_network_config(struct platform_powerpc *platform,
450         struct config *config)
451 {
452         unsigned int i;
453         char *val;
454
455         val = talloc_strdup(platform, "");
456
457         for (i = 0; i < config->network.n_interfaces; i++) {
458                 char *iface_str = iface_config_str(platform,
459                                         config->network.interfaces[i]);
460                 val = talloc_asprintf_append(val, "%s%s",
461                                 *val == '\0' ? "" : " ", iface_str);
462                 talloc_free(iface_str);
463         }
464
465         if (config->network.n_dns_servers) {
466                 char *dns_str = dns_config_str(platform,
467                                                 config->network.dns_servers,
468                                                 config->network.n_dns_servers);
469                 val = talloc_asprintf_append(val, "%s%s",
470                                 *val == '\0' ? "" : " ", dns_str);
471                 talloc_free(dns_str);
472         }
473
474         update_string_config(platform, "petitboot,network", val);
475
476         talloc_free(val);
477 }
478
479 static int update_config(struct platform_powerpc *platform,
480                 struct config *config, struct config *defaults)
481 {
482         char *tmp = NULL;
483         const char *val;
484
485         if (config->autoboot_enabled == defaults->autoboot_enabled)
486                 val = "";
487         else
488                 val = config->autoboot_enabled ? "true" : "false";
489         update_string_config(platform, "auto-boot?", val);
490
491         if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
492                 val = "";
493         else
494                 val = tmp = talloc_asprintf(platform, "%d",
495                                 config->autoboot_timeout_sec);
496
497         update_string_config(platform, "petitboot,timeout", val);
498         if (tmp)
499                 talloc_free(tmp);
500
501         update_network_config(platform, config);
502
503         return write_nvram(platform);
504 }
505
506 static int load_config(struct platform *p, struct config *config)
507 {
508         struct platform_powerpc *platform = to_platform_powerpc(p);
509         int rc;
510
511         rc = parse_nvram(platform);
512         if (rc)
513                 return rc;
514
515         populate_config(platform, config);
516
517         return 0;
518 }
519
520 static int save_config(struct platform *p, struct config *config)
521 {
522         struct platform_powerpc *platform = to_platform_powerpc(p);
523         struct config *defaults;
524         int rc;
525
526         defaults = talloc_zero(platform, struct config);
527         config_set_defaults(defaults);
528
529         rc = update_config(platform, config, defaults);
530
531         talloc_free(defaults);
532         return rc;
533 }
534
535 static bool probe(struct platform *p, void *ctx)
536 {
537         struct platform_powerpc *platform;
538
539         platform = talloc(ctx, struct platform_powerpc);
540         list_init(&platform->params);
541
542         p->platform_data = platform;
543         return true;
544 }
545
546 static struct platform platform_powerpc = {
547         .name           = "powerpc",
548         .probe          = probe,
549         .load_config    = load_config,
550         .save_config    = save_config,
551 };
552
553 register_platform(platform_powerpc);