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