lib/efi: Cleanup read/write routines
[petitboot] / discover / yaboot-parser.c
1 #if defined(HAVE_CONFIG_H)
2 #include "config.h"
3 #endif
4
5 #include <assert.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <i18n/i18n.h>
10
11 #include "log/log.h"
12 #include "talloc/talloc.h"
13 #include "types/types.h"
14 #include "parser-conf.h"
15 #include "parser-utils.h"
16 #include "resource.h"
17
18 struct yaboot_state {
19         int globals_done;
20
21         /* current option data */
22         struct discover_boot_option *opt;
23         const char *device;
24         const char *partition;
25         const char *boot_image;
26         const char *initrd;
27         const char *initrd_size;
28         const char *literal;
29         const char *ramdisk;
30         const char *root;
31         bool read_only;
32         bool read_write;
33 };
34
35 static struct discover_boot_option *state_start_new_option(
36                 struct conf_context *conf,
37                 struct yaboot_state *state)
38 {
39         state->opt = discover_boot_option_create(conf->dc, conf->dc->device);
40         state->opt->option->boot_args = talloc_strdup(state->opt->option, "");
41
42         /* old allocated values will get freed with the state */
43         state->device = conf_get_global_option(conf, "device");
44         state->partition = conf_get_global_option(conf, "partition");
45         state->initrd_size = conf_get_global_option(conf, "initrd_size");
46         state->literal = conf_get_global_option(conf, "literal");
47         state->ramdisk = conf_get_global_option(conf, "ramdisk");
48         state->root = conf_get_global_option(conf, "root");
49
50         return state->opt;
51 }
52
53 static struct resource *create_yaboot_devpath_resource(
54                 struct yaboot_state *state,
55                 struct conf_context *conf,
56                 const char *path)
57 {
58         struct discover_boot_option *opt = state->opt;
59         const char *dev, *part, *devpos;
60         struct resource *res;
61         char *devpath, *devstr;
62
63         dev = state->device;
64         part = state->partition;
65
66         if (!dev)
67                 dev = conf_get_global_option(conf, "device");
68         if (!part)
69                 part = conf_get_global_option(conf, "partition");
70
71         if (strchr(path, ':')) {
72                 devpath = talloc_strdup(conf, path);
73
74         } else if (dev && part) {
75                 devpos = &dev[strlen(dev) - 1];
76                 if (isdigit(*devpos)) {
77                         while (isdigit(*devpos))
78                                 devpos--;
79
80                         devstr = talloc_strndup(conf, dev, devpos - dev + 1);
81                         devpath = talloc_asprintf(conf, "%s%s:%s", devstr,
82                                         part, path);
83                         talloc_free(devstr);
84                 } else {
85                         devpath = talloc_asprintf(conf,
86                                         "%s%s:%s", dev, part, path);
87                 }
88         } else if (dev) {
89                 devpath = talloc_asprintf(conf, "%s:%s", dev, path);
90         } else {
91                 devpath = talloc_strdup(conf, path);
92         }
93
94         res = create_devpath_resource(opt, conf->dc->device, devpath);
95
96         talloc_free(devpath);
97
98         return res;
99 }
100
101 static void yaboot_finish(struct conf_context *conf)
102 {
103         struct yaboot_state *state = conf->parser_info;
104         const char *default_label;
105         struct boot_option *opt;
106
107         if (!state->opt)
108                 return;
109
110         opt = state->opt->option;
111         assert(opt);
112         assert(opt->name);
113         assert(opt->boot_args);
114
115         /* populate the boot option from state data */
116         state->opt->boot_image = create_yaboot_devpath_resource(state,
117                                 conf, state->boot_image);
118
119         char* args_sigfile_default = talloc_asprintf(opt,
120                 "%s.cmdline.sig", state->boot_image);
121         state->opt->args_sig_file = create_yaboot_devpath_resource(state,
122                                 conf, args_sigfile_default);
123         talloc_free(args_sigfile_default);
124
125         if (state->initrd) {
126                 state->opt->initrd = create_yaboot_devpath_resource(state,
127                                 conf, state->initrd);
128         }
129
130         if (state->initrd_size) {
131                 opt->boot_args = talloc_asprintf(opt, "ramdisk_size=%s %s",
132                                         state->initrd_size, opt->boot_args);
133         }
134
135         if (state->ramdisk) {
136                 opt->boot_args = talloc_asprintf(opt, "ramdisk=%s %s",
137                                         state->initrd_size, opt->boot_args);
138         }
139
140         if (state->root) {
141                 opt->boot_args = talloc_asprintf(opt, "root=%s %s",
142                                         state->root, opt->boot_args);
143         }
144
145         if (state->read_only && state->read_write) {
146                 pb_log("boot option %s specified both 'ro' and 'rw', "
147                                 "using 'rw'\n", opt->name);
148                 state->read_only = false;
149         }
150
151         if (state->read_only || state->read_write) {
152                 opt->boot_args = talloc_asprintf(opt, "%s %s",
153                                         state->read_only ? "ro" : "rw",
154                                         opt->boot_args);
155         }
156
157         if (state->literal) {
158                 opt->boot_args = talloc_strdup(opt, state->literal);
159         }
160
161         opt->description = talloc_asprintf(opt, "%s %s %s",
162                 state->boot_image,
163                 (state->initrd ? state->initrd : ""),
164                 opt->boot_args ? opt->boot_args : "");
165
166         conf_strip_str(opt->boot_args);
167         conf_strip_str(opt->description);
168
169         default_label = conf_get_global_option(conf, "default");
170         if (default_label &&
171                         !strcasecmp(state->opt->option->name, default_label))
172                 state->opt->option->is_default = true;
173
174         discover_context_add_boot_option(conf->dc, state->opt);
175         state->opt = NULL;
176 }
177
178 static void yaboot_process_pair(struct conf_context *conf, const char *name,
179                 char *value)
180 {
181         struct yaboot_state *state = conf->parser_info;
182         struct discover_boot_option *opt = state->opt;
183         struct fixed_pair {
184                 const char *image;
185                 const char *initrd;
186         };
187         static const struct fixed_pair suse_fp32 = {
188                 .image = "/suseboot/vmlinux32",
189                 .initrd = "/suseboot/initrd32",
190         };
191         static const struct fixed_pair suse_fp64 = {
192                 .image = "/suseboot/vmlinux64",
193                 .initrd = "/suseboot/initrd64",
194         };
195         const struct fixed_pair *suse_fp;
196
197         /* fixup for bare values */
198
199         if (!name)
200                 name = value;
201
202         if (!state->globals_done && conf_set_global_option(conf, name, value))
203                 return;
204
205         /* image */
206         if (streq(name, "image")) {
207                 /* an image section finishes our global defintions */
208                 state->globals_done = 1;
209
210                 /* First finish any previous image. */
211                 if (opt)
212                         yaboot_finish(conf);
213
214                 /* Then start the new image. */
215                 opt = state_start_new_option(conf, state);
216
217                 state->boot_image = talloc_strdup(state, value);
218
219                 return;
220         }
221
222         /* Special processing for SUSE install CD. */
223
224         if (streq(name, "image[32bit]"))
225                 suse_fp = &suse_fp32;
226         else if (streq(name, "image[64bit]"))
227                 suse_fp = &suse_fp64;
228         else
229                 suse_fp = NULL;
230
231         if (suse_fp) {
232                 /* First finish any previous image. */
233                 if (opt)
234                         yaboot_finish(conf);
235
236                 /* Then start the new image. */
237                 opt = state_start_new_option(conf, state);
238
239                 if (*value == '/') {
240                         state->boot_image = talloc_strdup(state, value);
241                 } else {
242                         state->boot_image = talloc_strdup(state,
243                                                         suse_fp->image);
244                         state->initrd = talloc_strdup(state, suse_fp->initrd);
245                 }
246
247                 return;
248         }
249
250         /* all other processing requires an image */
251         if (!opt) {
252                 pb_debug("%s: unknown name: %s\n", __func__, name);
253                 return;
254         }
255
256         /* initrd */
257         if (streq(name, "initrd")) {
258                 state->initrd = talloc_strdup(state, value);
259                 return;
260         }
261
262         /* label */
263         if (streq(name, "label")) {
264                 opt->option->id = talloc_asprintf(opt->option, "%s#%s",
265                         conf->dc->device->device->id, value);
266                 opt->option->name = talloc_strdup(opt->option, value);
267                 return;
268         }
269
270         /* args */
271         if (streq(name, "device")) {
272                 printf("option device : %s", value);
273                 state->device = talloc_strdup(state, value);
274                 return;
275         }
276
277         if (streq(name, "parititon")) {
278                 state->partition = talloc_strdup(state, value);
279                 return;
280         }
281
282         if (streq(name, "append")) {
283                 opt->option->boot_args = talloc_asprintf_append(
284                         opt->option->boot_args, "%s ", value);
285                 return;
286         }
287
288         if (streq(name, "initrd-size")) {
289                 state->initrd_size = talloc_strdup(state, value);
290                 return;
291         }
292
293         if (streq(name, "literal")) {
294                 state->literal = talloc_strdup(state, value);
295                 return;
296         }
297
298         if (streq(name, "ramdisk")) {
299                 state->ramdisk = talloc_strdup(state, value);
300                 return;
301         }
302
303         if (streq(name, "read-only")) {
304                 state->read_only = true;
305                 return;
306         }
307
308         if (streq(name, "read-write")) {
309                 state->read_write = true;
310                 return;
311         }
312
313         if (streq(name, "root")) {
314                 state->root = talloc_strdup(state, value);
315                 return;
316         }
317
318         pb_debug("%s: unknown name: %s\n", __func__, name);
319 }
320
321 static struct conf_global_option yaboot_global_options[] = {
322         { .name = "root" },
323         { .name = "device" },
324         { .name = "partition" },
325         { .name = "initrd" },
326         { .name = "initrd_size" },
327         { .name = "video" },
328         { .name = "literal" },
329         { .name = "ramdisk" },
330         { .name = "default" },
331         { .name = NULL },
332 };
333
334 static const char *const yaboot_conf_files[] = {
335         "/yaboot.conf",
336         "/yaboot.cnf",
337         "/etc/lilo.conf",
338         "/etc/silo.conf",
339         "/etc/yaboot.conf",
340         "/etc/yaboot.cnf",
341         "/suseboot/yaboot.cnf",
342         "/YABOOT.CONF",
343         "/YABOOT.CNF",
344         "/ETC/LILO.CONF",
345         "/ETC/SILO.CONF",
346         "/ETC/YABOOT.CONF",
347         "/ETC/YABOOT.CNF",
348         "/SUSEBOOT/YABOOT.CNF",
349         NULL
350 };
351
352 static int yaboot_parse(struct discover_context *dc)
353 {
354         const char * const *filename;
355         struct yaboot_state *state;
356         struct conf_context *conf;
357         int len, rc;
358         char *buf;
359
360         /* Support block device boot only at present */
361         if (dc->event)
362                 return -1;
363
364         conf = talloc_zero(dc, struct conf_context);
365
366         if (!conf)
367                 return -1;
368
369         conf->dc = dc;
370         conf->global_options = yaboot_global_options,
371         conf_init_global_options(conf);
372         conf->get_pair = conf_get_pair_equal;
373         conf->process_pair = yaboot_process_pair;
374         conf->finish = yaboot_finish;
375         conf->parser_info = state = talloc_zero(conf, struct yaboot_state);
376
377         state->opt = NULL;
378
379         for (filename = yaboot_conf_files; *filename; filename++) {
380                 rc = parser_request_file(dc, dc->device, *filename, &buf, &len);
381                 if (rc)
382                         continue;
383
384                 conf_parse_buf(conf, buf, len);
385                 device_handler_status_dev_info(dc->handler, dc->device,
386                                 _("Parsed yaboot configuration from %s"),
387                                 *filename);
388                 talloc_free(buf);
389         }
390
391         talloc_free(conf);
392         return 0;
393 }
394
395 static struct parser yaboot_parser = {
396         .name                   = "yaboot",
397         .parse                  = yaboot_parse,
398         .resolve_resource       = resolve_devpath_resource,
399 };
400
401 register_parser(yaboot_parser);