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