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