]> git.ozlabs.org Git - petitboot/blob - devices/kboot-parser.c
Separate parsing infrastructure and add test wrapper
[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 static int parse_option(struct boot_option *opt, char *config)
80 {
81         char *pos, *name, *value, *root, *initrd, *cmdline, *tmp;
82
83         root = initrd = cmdline = NULL;
84
85         /* remove quotes around the value */
86         while (*config == '"' || *config == '\'')
87                 config++;
88
89         pos = config + strlen(config) - 1;
90         while (*pos == '"' || *pos == '\'')
91                 *(pos--) = 0;
92
93         if (!strlen(pos))
94                 return 0;
95
96         pos = strchr(config, ' ');
97
98         /* if there's no space, it's only a kernel image with no params */
99         if (!pos) {
100                 opt->boot_image_file = join_paths(mountpoint, config);
101                 opt->description = strdup(config);
102                 return 1;
103         }
104
105         *pos = 0;
106         opt->boot_image_file = join_paths(mountpoint, config);
107
108         cmdline = malloc(buf_size);
109
110         for (pos++; pos;) {
111                 pos = get_param_pair(pos, &name, &value, ' ');
112
113                 if (!name) {
114                         strcat(cmdline, " ");
115                         strcat(cmdline, value);
116
117                 } else if (streq(name, "initrd")) {
118                         initrd = value;
119
120                 } else if (streq(name, "root")) {
121                         root = value;
122
123                 } else {
124                         strcat(cmdline, " ");
125                         *(value - 1) = '=';
126                         strcat(cmdline, name);
127                 }
128         }
129
130         if (initrd) {
131                 asprintf(&tmp, "initrd=%s %s", initrd, cmdline);
132                 free(cmdline);
133                 cmdline = tmp;
134
135                 opt->initrd_file = join_paths(mountpoint, initrd);
136         }
137
138         if (root) {
139                 asprintf(&tmp, "root=%s %s", root, cmdline);
140                 free(cmdline);
141                 cmdline = tmp;
142
143         } else if (!initrd) {
144                 /* if there's an initrd but no root, fake up /dev/ram0 */
145                 asprintf(&tmp, "root=/dev/ram0 %s", cmdline);
146                 free(cmdline);
147                 cmdline = tmp;
148         }
149
150         printf("kboot cmdline: %s", cmdline);
151         opt->boot_args = cmdline;
152
153         asprintf(&opt->description, "%s %s", config, cmdline);
154
155         return 1;
156 }
157
158 static void parse_buf(struct device *dev, char *buf)
159 {
160         char *pos, *name, *value;
161         int sent_device = 0;
162
163         for (pos = buf; pos;) {
164                 struct boot_option opt;
165
166                 pos = get_param_pair(pos, &name, &value, '\n');
167
168                 printf("kboot param: '%s' = '%s'\n", name, value);
169
170                 if (name == NULL || param_is_ignored(name))
171                         continue;
172
173                 memset(&opt, 0, sizeof(opt));
174                 opt.name = strdup(name);
175
176                 if (parse_option(&opt, value))
177                         if (!sent_device++)
178                                 add_device(dev);
179                         add_boot_option(&opt);
180
181                 free(opt.name);
182         }
183 }
184
185 static int parse(const char *devicepath, const char *_mountpoint)
186 {
187         char *filepath, *buf;
188         int fd, len, rc = 0;
189         struct stat stat;
190         struct device *dev;
191
192         mountpoint = _mountpoint;
193
194         filepath = join_paths(mountpoint, "/etc/kboot.conf");
195
196         fd = open(filepath, O_RDONLY);
197         if (fd < 0)
198                 goto out_free_path;
199
200         if (fstat(fd, &stat))
201                 goto out_close;
202
203         buf = malloc(stat.st_size + 1);
204         if (!buf)
205                 goto out_close;;
206
207         len = read(fd, buf, stat.st_size);
208         if (len < 0)
209                 goto out_free_buf;
210         buf[len] = 0;
211
212         dev = malloc(sizeof(*dev));
213         memset(dev, 0, sizeof(*dev));
214         dev->id = strdup(devicepath);
215         dev->icon_file = strdup(generic_icon_file(guess_device_type()));
216
217         parse_buf(dev, buf);
218
219         rc = 1;
220
221 out_free_buf:
222         free(buf);
223 out_close:
224         close(fd);
225 out_free_path:
226         free(filepath);
227         return rc;
228 }
229
230 struct parser kboot_parser = {
231         .name = "kboot.conf parser",
232         .priority = 98,
233         .parse    = parse
234 };