0f69f7e291060550df492785cf402e83b6ead78f
[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 #define BLS_DIR "/loader/entries"
19
20 struct bls_state {
21         struct discover_boot_option *opt;
22         struct grub2_script *script;
23         const char *filename;
24         const char *title;
25         const char *version;
26         const char *machine_id;
27         const char *image;
28         const char *initrd;
29         const char *dtb;
30 };
31
32 static void bls_process_pair(struct conf_context *conf, const char *name,
33                              char *value)
34 {
35         struct bls_state *state = conf->parser_info;
36         struct discover_boot_option *opt = state->opt;
37         struct boot_option *option = opt->option;
38         const char *boot_args;
39
40         if (streq(name, "title")) {
41                 state->title = talloc_strdup(state, value);
42                 return;
43         }
44
45         if (streq(name, "version")) {
46                 state->version = talloc_strdup(state, value);
47                 return;
48         }
49
50         if (streq(name, "machine-id")) {
51                 state->machine_id = talloc_strdup(state, value);
52                 return;
53         }
54
55         if (streq(name, "linux")) {
56                 state->image = talloc_strdup(state, value);
57                 return;
58         }
59
60         if (streq(name, "initrd")) {
61                 state->initrd = talloc_strdup(state, value);
62                 return;
63         }
64
65         if (streq(name, "devicetree")) {
66                 state->dtb = talloc_strdup(state, value);
67                 return;
68         }
69
70         if (streq(name, "options")) {
71                 if (value[0] == '$') {
72                         boot_args = script_env_get(state->script, value + 1);
73                         if (!boot_args)
74                                 return;
75
76                         option->boot_args = talloc_strdup(opt, boot_args);
77                 } else {
78                         option->boot_args = talloc_strdup(opt, value);
79                 }
80                 return;
81         }
82 }
83
84 static bool option_is_default(struct grub2_script *script,
85                               struct boot_option *option)
86 {
87         const char *var;
88
89         var = script_env_get(script, "default");
90         if (!var)
91                 return false;
92
93         if (!strcmp(var, option->id))
94                 return true;
95
96         return !strcmp(var, option->name);
97 }
98
99 static void bls_finish(struct conf_context *conf)
100 {
101         struct bls_state *state = conf->parser_info;
102         struct discover_context *dc = conf->dc;
103         struct discover_boot_option *opt = state->opt;
104         struct boot_option *option = opt->option;
105         const char *root;
106         char *filename;
107
108         if (!state->image) {
109                 device_handler_status_dev_info(dc->handler, dc->device,
110                                                _("linux field not found in %s"),
111                                                state->filename);
112                 return;
113         }
114
115         filename = basename(state->filename);
116         filename[strlen(filename) - strlen(".conf")] = '\0';
117
118         option->id = talloc_strdup(option, filename);
119
120         if (state->title)
121                 option->name = talloc_strdup(option, state->title);
122         else if (state->machine_id && state->version)
123                 option->name = talloc_asprintf(option, "%s %s",
124                                                state->machine_id,
125                                                state->version);
126         else if (state->version)
127                 option->name = talloc_strdup(option, state->version);
128         else
129                 option->name = talloc_strdup(option, state->image);
130
131         root = script_env_get(state->script, "root");
132
133         opt->boot_image = create_grub2_resource(opt, conf->dc->device,
134                                                 root, state->image);
135
136         if (state->initrd)
137                 opt->initrd = create_grub2_resource(opt, conf->dc->device,
138                                                     root, state->initrd);
139
140         if (state->dtb)
141                 opt->dtb = create_grub2_resource(opt, conf->dc->device,
142                                                  root, state->dtb);
143
144         option->is_default = option_is_default(state->script, option);
145
146         discover_context_add_boot_option(dc, opt);
147
148         device_handler_status_dev_info(dc->handler, dc->device,
149                                        _("Created menu entry from BLS file %s"),
150                                        state->filename);
151 }
152
153 static int bls_filter(const struct dirent *ent)
154 {
155         int offset = strlen(ent->d_name) - strlen(".conf");
156
157         if (offset < 0)
158                 return 0;
159
160         return strncmp(ent->d_name + offset, ".conf", strlen(".conf")) == 0;
161 }
162
163 static int bls_sort(const struct dirent **ent_a, const struct dirent **ent_b)
164 {
165         return strverscmp((*ent_b)->d_name, (*ent_a)->d_name);
166 }
167
168 int builtin_blscfg(struct grub2_script *script,
169                 void *data __attribute__((unused)),
170                 int argc __attribute__((unused)),
171                 char *argv[] __attribute__((unused)));
172
173 int builtin_blscfg(struct grub2_script *script,
174                 void *data __attribute__((unused)),
175                 int argc __attribute__((unused)),
176                 char *argv[] __attribute__((unused)))
177 {
178         struct discover_context *dc = script->ctx;
179         struct dirent **bls_entries;
180         struct conf_context *conf;
181         struct bls_state *state;
182         char *buf, *filename;
183         int n, len, rc = -1;
184
185         conf = talloc_zero(dc, struct conf_context);
186         if (!conf)
187                 return rc;
188
189         conf->dc = dc;
190         conf->get_pair = conf_get_pair_space;
191         conf->process_pair = bls_process_pair;
192         conf->finish = bls_finish;
193
194         n = parser_scandir(dc, BLS_DIR, &bls_entries, bls_filter, bls_sort);
195         if (n <= 0)
196                 goto err;
197
198         while (n--) {
199                 filename = talloc_asprintf(dc, BLS_DIR"/%s",
200                                            bls_entries[n]->d_name);
201                 if (!filename)
202                         break;
203
204                 state = talloc_zero(conf, struct bls_state);
205                 if (!state)
206                         break;
207
208                 state->opt = discover_boot_option_create(dc, dc->device);
209                 if (!state->opt)
210                         break;
211
212                 state->script = script;
213                 state->filename = filename;
214                 conf->parser_info = state;
215
216                 rc = parser_request_file(dc, dc->device, filename, &buf, &len);
217                 if (rc)
218                         break;
219
220                 conf_parse_buf(conf, buf, len);
221
222                 talloc_free(buf);
223                 talloc_free(state);
224                 talloc_free(filename);
225                 free(bls_entries[n]);
226         }
227
228         if (n > 0) {
229                 device_handler_status_dev_info(dc->handler, dc->device,
230                                                _("Scanning %s failed"),
231                                                BLS_DIR);
232                 do {
233                         free(bls_entries[n]);
234                 } while (n-- > 0);
235         }
236
237         free(bls_entries);
238 err:
239         talloc_free(conf);
240         return rc;
241 }