discover/grub: Use different paths to search for the BLS directory
[petitboot] / discover / grub2 / blscfg.c
1
2 #define _GNU_SOURCE
3
4 #include <assert.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <dirent.h>
8
9 #include <log/log.h>
10 #include <file/file.h>
11 #include <talloc/talloc.h>
12 #include <i18n/i18n.h>
13
14 #include "grub2.h"
15 #include "discover/parser-conf.h"
16 #include "discover/parser.h"
17
18 static const char *const bls_dirs[] = {
19         "/loader/entries",
20         "/boot/loader/entries",
21         NULL
22 };
23
24 struct bls_state {
25         struct discover_boot_option *opt;
26         struct grub2_script *script;
27         unsigned int idx;
28         const char *filename;
29         const char *title;
30         const char *version;
31         const char *machine_id;
32         const char *image;
33         const char *initrd;
34         const char *dtb;
35 };
36
37 static void bls_process_pair(struct conf_context *conf, const char *name,
38                              char *value)
39 {
40         struct bls_state *state = conf->parser_info;
41         struct discover_boot_option *opt = state->opt;
42         struct boot_option *option = opt->option;
43         const char *boot_args;
44
45         if (streq(name, "title")) {
46                 state->title = talloc_strdup(state, value);
47                 return;
48         }
49
50         if (streq(name, "version")) {
51                 state->version = talloc_strdup(state, value);
52                 return;
53         }
54
55         if (streq(name, "machine-id")) {
56                 state->machine_id = talloc_strdup(state, value);
57                 return;
58         }
59
60         if (streq(name, "linux")) {
61                 state->image = talloc_strdup(state, value);
62                 return;
63         }
64
65         if (streq(name, "initrd")) {
66                 state->initrd = talloc_strdup(state, value);
67                 return;
68         }
69
70         if (streq(name, "devicetree")) {
71                 state->dtb = talloc_strdup(state, value);
72                 return;
73         }
74
75         if (streq(name, "options")) {
76                 if (value[0] == '$') {
77                         boot_args = script_env_get(state->script, value + 1);
78                         if (!boot_args)
79                                 return;
80
81                         option->boot_args = talloc_strdup(opt, boot_args);
82                 } else {
83                         option->boot_args = talloc_strdup(opt, value);
84                 }
85                 return;
86         }
87 }
88
89 static bool option_is_default(struct bls_state *state,
90                               struct boot_option *option)
91 {
92         unsigned int idx;
93         const char *var;
94         char *end;
95
96         var = script_env_get(state->script, "default");
97         if (!var)
98                 return false;
99
100         if (!strcmp(var, option->id))
101                 return true;
102
103         if (!strcmp(var, option->name))
104                 return true;
105
106         idx = strtoul(var, &end, 10);
107         return end != var && *end == '\0' && idx == state->idx;
108 }
109
110 static void bls_finish(struct conf_context *conf)
111 {
112         struct bls_state *state = conf->parser_info;
113         struct discover_context *dc = conf->dc;
114         struct discover_boot_option *opt = state->opt;
115         struct boot_option *option = opt->option;
116         const char *root;
117         char *filename;
118
119         if (!state->image) {
120                 device_handler_status_dev_info(dc->handler, dc->device,
121                                                _("linux field not found in %s"),
122                                                state->filename);
123                 return;
124         }
125
126         filename = basename(state->filename);
127         filename[strlen(filename) - strlen(".conf")] = '\0';
128
129         option->id = talloc_strdup(option, filename);
130
131         if (state->title)
132                 option->name = talloc_strdup(option, state->title);
133         else if (state->machine_id && state->version)
134                 option->name = talloc_asprintf(option, "%s %s",
135                                                state->machine_id,
136                                                state->version);
137         else if (state->version)
138                 option->name = talloc_strdup(option, state->version);
139         else
140                 option->name = talloc_strdup(option, state->image);
141
142         root = script_env_get(state->script, "root");
143
144         opt->boot_image = create_grub2_resource(opt, conf->dc->device,
145                                                 root, state->image);
146
147         if (state->initrd)
148                 opt->initrd = create_grub2_resource(opt, conf->dc->device,
149                                                     root, state->initrd);
150
151         if (state->dtb)
152                 opt->dtb = create_grub2_resource(opt, conf->dc->device,
153                                                  root, state->dtb);
154
155         char* args_sigfile_default = talloc_asprintf(opt,
156                 "%s.cmdline.sig", state->image);
157         opt->args_sig_file = create_grub2_resource(opt, conf->dc->device,
158                                                 root, args_sigfile_default);
159         talloc_free(args_sigfile_default);
160
161         option->is_default = option_is_default(state, option);
162
163         list_add_tail(&state->script->options, &opt->list);
164         state->script->n_options++;
165
166         device_handler_status_dev_info(dc->handler, dc->device,
167                                        _("Created menu entry from BLS file %s"),
168                                        state->filename);
169 }
170
171 static int bls_filter(const struct dirent *ent)
172 {
173         int offset = strlen(ent->d_name) - strlen(".conf");
174
175         if (offset < 0)
176                 return 0;
177
178         return strncmp(ent->d_name + offset, ".conf", strlen(".conf")) == 0;
179 }
180
181 static int bls_sort(const struct dirent **ent_a, const struct dirent **ent_b)
182 {
183         return strverscmp((*ent_a)->d_name, (*ent_b)->d_name);
184 }
185
186 int builtin_blscfg(struct grub2_script *script,
187                 void *data __attribute__((unused)),
188                 int argc __attribute__((unused)),
189                 char *argv[] __attribute__((unused)));
190
191 int builtin_blscfg(struct grub2_script *script,
192                 void *data __attribute__((unused)),
193                 int argc __attribute__((unused)),
194                 char *argv[] __attribute__((unused)))
195 {
196         unsigned int current_idx = script->n_options;
197         struct discover_context *dc = script->ctx;
198         struct dirent **bls_entries;
199         struct conf_context *conf;
200         struct bls_state *state;
201         char *buf, *filename;
202         const char * const *dir;
203         const char *blsdir;
204         int n, len, rc = -1;
205         struct stat statbuf;
206
207         conf = talloc_zero(dc, struct conf_context);
208         if (!conf)
209                 return rc;
210
211         conf->dc = dc;
212         conf->get_pair = conf_get_pair_space;
213         conf->process_pair = bls_process_pair;
214         conf->finish = bls_finish;
215
216         blsdir = script_env_get(script, "blsdir");
217         if (!blsdir)
218                 for (dir = bls_dirs; *dir; dir++)
219                         if (!parser_stat_path(dc, dc->device, *dir, &statbuf)) {
220                                 blsdir = *dir;
221                                 break;
222                         }
223
224         if (!blsdir) {
225                 device_handler_status_dev_info(dc->handler, dc->device,
226                                                _("BLS directory wasn't found"));
227                 goto err;
228         }
229
230         n = parser_scandir(dc, blsdir, &bls_entries, bls_filter, bls_sort);
231         if (n <= 0)
232                 goto err;
233
234         while (n--) {
235                 filename = talloc_asprintf(dc, "%s/%s", blsdir,
236                                            bls_entries[n]->d_name);
237                 if (!filename)
238                         break;
239
240                 state = talloc_zero(conf, struct bls_state);
241                 if (!state)
242                         break;
243
244                 state->opt = discover_boot_option_create(dc, dc->device);
245                 if (!state->opt)
246                         break;
247
248                 state->script = script;
249                 state->filename = filename;
250                 state->idx = current_idx++;
251                 conf->parser_info = state;
252
253                 rc = parser_request_file(dc, dc->device, filename, &buf, &len);
254                 if (rc)
255                         break;
256
257                 conf_parse_buf(conf, buf, len);
258
259                 talloc_free(buf);
260                 talloc_free(state);
261                 talloc_free(filename);
262                 free(bls_entries[n]);
263         }
264
265         if (n > 0) {
266                 device_handler_status_dev_info(dc->handler, dc->device,
267                                                _("Scanning %s failed"),
268                                                blsdir);
269                 do {
270                         free(bls_entries[n]);
271                 } while (n-- > 0);
272         }
273
274         free(bls_entries);
275 err:
276         talloc_free(conf);
277         return rc;
278 }