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