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