]> git.ozlabs.org Git - petitboot/blob - devices/kboot-parser.c
kboot parser: add facility for default options
[petitboot] / devices / kboot-parser.c
1 #define _GNU_SOURCE
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <ctype.h>
7 #include <unistd.h>
8
9 #include <fcntl.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12
13 #include "parser.h"
14 #include "params.h"
15
16 #define buf_size 1024
17
18 static const char *mountpoint;
19
20 static int param_is_ignored(const char *param)
21 {
22         static const char *ignored_options[] =
23                 { "message", "timeout", "default", NULL };
24         const char **str;
25
26         for (str = ignored_options; *str; str++)
27                 if (streq(*str, param))
28                         return 1;
29         return 0;
30 }
31
32 /**
33  * Splits a name=value pair, with value terminated by @term (or nul). if there
34  * is no '=', then only the value is populated, and *name is set to NULL. The
35  * string is modified in place.
36  *
37  * Returns the next byte to process, or null if we've hit the end of the
38  * string.
39  *
40  * */
41 static char *get_param_pair(char *str, char **name_out, char **value_out,
42                 char terminator)
43 {
44         char *sep, *tmp, *name, *value;
45
46         /* terminate the value */
47         tmp = strchr(str, terminator);
48         if (tmp)
49                 *tmp = 0;
50         else
51                 tmp = NULL;
52
53         sep = strchr(str, '=');
54         if (!sep) {
55                 *name_out = NULL;
56                 *value_out = str;
57                 return tmp ? tmp + 1 : NULL;
58         }
59
60         /* terminate the name */
61         *sep = 0;
62
63         /* remove leading spaces */
64         for (name = str; isspace(*name); name++);
65         for (value = sep + 1; isspace(*value); value++);
66
67         /* .. and trailing ones.. */
68         for (sep--; isspace(*sep); sep--)
69                 *sep = 0;
70         for (sep = value + strlen(value) - 1; isspace(*sep); sep--)
71                 *sep = 0;
72
73         *name_out = name;
74         *value_out = value;
75
76         return tmp ? tmp + 1 : NULL;
77 }
78
79 struct global_option {
80         char *name;
81         char *value;
82 };
83
84
85 struct global_option global_options[] = {
86         { .name = "root" },
87         { .name = "initrd" },
88         { .name = "video" },
89         { .name = NULL }
90 };
91
92 /*
93  * Check if an option (name=value) is a global option. If so, store it in
94  * the global options table, and return 1. Otherwise, return 0.
95  */
96 static int check_for_global_option(const char *name, const char *value)
97 {
98         int i;
99
100         for (i = 0; global_options[i].name ;i++) {
101                 if (!strcmp(name, global_options[i].name)) {
102                         global_options[i].value = strdup(value);
103                         return 1;
104                 }
105         }
106         return 0;
107 }
108
109 static char *get_global_option(const char *name)
110 {
111         int i;
112
113         for (i = 0; global_options[i].name ;i++)
114                 if (!strcmp(name, global_options[i].name))
115                         return global_options[i].value;
116
117         return NULL;
118 }
119
120 static int parse_option(struct boot_option *opt, char *config)
121 {
122         char *pos, *name, *value, *root, *initrd, *cmdline, *tmp;
123
124         root = initrd = cmdline = NULL;
125
126         /* remove quotes around the value */
127         while (*config == '"' || *config == '\'')
128                 config++;
129
130         pos = config + strlen(config) - 1;
131         while (*pos == '"' || *pos == '\'')
132                 *(pos--) = 0;
133
134         if (!strlen(pos))
135                 return 0;
136
137         pos = strchr(config, ' ');
138
139         /* if there's no space, it's only a kernel image with no params */
140         if (!pos) {
141                 opt->boot_image_file = join_paths(mountpoint, config);
142                 opt->description = strdup(config);
143                 return 1;
144         }
145
146         *pos = 0;
147         opt->boot_image_file = join_paths(mountpoint, config);
148
149         cmdline = malloc(buf_size);
150         *cmdline = 0;
151
152         for (pos++; pos;) {
153                 pos = get_param_pair(pos, &name, &value, ' ');
154
155                 if (!name) {
156                         strcat(cmdline, " ");
157                         strcat(cmdline, value);
158
159                 } else if (streq(name, "initrd")) {
160                         initrd = value;
161
162                 } else if (streq(name, "root")) {
163                         root = value;
164
165                 } else {
166                         strcat(cmdline, " ");
167                         *(value - 1) = '=';
168                         strcat(cmdline, name);
169                 }
170         }
171
172         if (!root)
173                 root = get_global_option("root");
174         if (!initrd)
175                 initrd = get_global_option("initrd");
176
177         if (initrd) {
178                 asprintf(&tmp, "initrd=%s %s", initrd, cmdline);
179                 free(cmdline);
180                 cmdline = tmp;
181
182                 opt->initrd_file = join_paths(mountpoint, initrd);
183         }
184
185         if (root) {
186                 asprintf(&tmp, "root=%s %s", root, cmdline);
187                 free(cmdline);
188                 cmdline = tmp;
189
190         } else if (initrd) {
191                 /* if there's an initrd but no root, fake up /dev/ram0 */
192                 asprintf(&tmp, "root=/dev/ram0 %s", cmdline);
193                 free(cmdline);
194                 cmdline = tmp;
195         }
196
197         pb_log("kboot cmdline: %s\n", cmdline);
198         opt->boot_args = cmdline;
199
200         asprintf(&opt->description, "%s %s", config, cmdline);
201
202         return 1;
203 }
204
205 static void parse_buf(struct device *dev, char *buf)
206 {
207         char *pos, *name, *value;
208         int sent_device = 0;
209
210         for (pos = buf; pos;) {
211                 struct boot_option opt;
212
213                 pos = get_param_pair(pos, &name, &value, '\n');
214
215                 pb_log("kboot param: '%s' = '%s'\n", name, value);
216
217                 if (name == NULL || param_is_ignored(name))
218                         continue;
219
220                 if (*name == '#')
221                         continue;
222
223                 if (check_for_global_option(name, value))
224                         continue;
225
226                 memset(&opt, 0, sizeof(opt));
227                 opt.name = strdup(name);
228
229                 if (parse_option(&opt, value))
230                         if (!sent_device++)
231                                 add_device(dev);
232                         add_boot_option(&opt);
233
234                 free(opt.name);
235         }
236 }
237
238 static int parse(const char *devicepath, const char *_mountpoint)
239 {
240         char *filepath, *buf;
241         int fd, len, rc = 0;
242         struct stat stat;
243         struct device *dev;
244
245         mountpoint = _mountpoint;
246
247         filepath = join_paths(mountpoint, "/etc/kboot.conf");
248
249         fd = open(filepath, O_RDONLY);
250         if (fd < 0)
251                 goto out_free_path;
252
253         if (fstat(fd, &stat))
254                 goto out_close;
255
256         buf = malloc(stat.st_size + 1);
257         if (!buf)
258                 goto out_close;;
259
260         len = read(fd, buf, stat.st_size);
261         if (len < 0)
262                 goto out_free_buf;
263         buf[len] = 0;
264
265         dev = malloc(sizeof(*dev));
266         memset(dev, 0, sizeof(*dev));
267         dev->id = strdup(devicepath);
268         dev->icon_file = strdup(generic_icon_file(guess_device_type()));
269
270         parse_buf(dev, buf);
271
272         rc = 1;
273
274 out_free_buf:
275         free(buf);
276 out_close:
277         close(fd);
278 out_free_path:
279         free(filepath);
280         return rc;
281 }
282
283 struct parser kboot_parser = {
284         .name = "kboot.conf parser",
285         .priority = 98,
286         .parse    = parse
287 };