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