]> git.ozlabs.org Git - petitboot/blob - discover/grub2/blscfg.c
5dadd2c6e8efde1ccf2eaf37af2893f4825c1cef
[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         list_add_tail(&state->script->options, &opt->list);
147         state->script->n_options++;
148
149         device_handler_status_dev_info(dc->handler, dc->device,
150                                        _("Created menu entry from BLS file %s"),
151                                        state->filename);
152 }
153
154 static int bls_filter(const struct dirent *ent)
155 {
156         int offset = strlen(ent->d_name) - strlen(".conf");
157
158         if (offset < 0)
159                 return 0;
160
161         return strncmp(ent->d_name + offset, ".conf", strlen(".conf")) == 0;
162 }
163
164 static int bls_sort(const struct dirent **ent_a, const struct dirent **ent_b)
165 {
166         return strverscmp((*ent_a)->d_name, (*ent_b)->d_name);
167 }
168
169 int builtin_blscfg(struct grub2_script *script,
170                 void *data __attribute__((unused)),
171                 int argc __attribute__((unused)),
172                 char *argv[] __attribute__((unused)));
173
174 int builtin_blscfg(struct grub2_script *script,
175                 void *data __attribute__((unused)),
176                 int argc __attribute__((unused)),
177                 char *argv[] __attribute__((unused)))
178 {
179         struct discover_context *dc = script->ctx;
180         struct dirent **bls_entries;
181         struct conf_context *conf;
182         struct bls_state *state;
183         char *buf, *filename;
184         const char *blsdir;
185         int n, len, rc = -1;
186
187         conf = talloc_zero(dc, struct conf_context);
188         if (!conf)
189                 return rc;
190
191         conf->dc = dc;
192         conf->get_pair = conf_get_pair_space;
193         conf->process_pair = bls_process_pair;
194         conf->finish = bls_finish;
195
196         blsdir = script_env_get(script, "blsdir");
197         if (!blsdir)
198                 blsdir = BLS_DIR;
199
200         n = parser_scandir(dc, blsdir, &bls_entries, bls_filter, bls_sort);
201         if (n <= 0)
202                 goto err;
203
204         while (n--) {
205                 filename = talloc_asprintf(dc, "%s/%s", blsdir,
206                                            bls_entries[n]->d_name);
207                 if (!filename)
208                         break;
209
210                 state = talloc_zero(conf, struct bls_state);
211                 if (!state)
212                         break;
213
214                 state->opt = discover_boot_option_create(dc, dc->device);
215                 if (!state->opt)
216                         break;
217
218                 state->script = script;
219                 state->filename = filename;
220                 conf->parser_info = state;
221
222                 rc = parser_request_file(dc, dc->device, filename, &buf, &len);
223                 if (rc)
224                         break;
225
226                 conf_parse_buf(conf, buf, len);
227
228                 talloc_free(buf);
229                 talloc_free(state);
230                 talloc_free(filename);
231                 free(bls_entries[n]);
232         }
233
234         if (n > 0) {
235                 device_handler_status_dev_info(dc->handler, dc->device,
236                                                _("Scanning %s failed"),
237                                                BLS_DIR);
238                 do {
239                         free(bls_entries[n]);
240                 } while (n-- > 0);
241         }
242
243         free(bls_entries);
244 err:
245         talloc_free(conf);
246         return rc;
247 }