c218bc7d07155bd6426d630f26bf623f0bcb4bb6
[petitboot] / discover / grub2 / builtins.c
1
2 #include <stdio.h>
3 #include <string.h>
4
5 #include <log/log.h>
6 #include <types/types.h>
7 #include <talloc/talloc.h>
8 #include <array-size/array-size.h>
9
10 #include "grub2.h"
11
12
13 static int builtin_set(struct grub2_script *script,
14                 void *data __attribute__((unused)),
15                 int argc, char *argv[])
16 {
17         char *name, *value, *p;
18         int i;
19
20         if (argc < 2)
21                 return -1;
22
23         p = strchr(argv[1], '=');
24         if (!p)
25                 return -1;
26
27         name = talloc_strndup(script, argv[1], p - argv[1]);
28         value = talloc_strdup(script, p+1);
29
30         for (i = 2; i < argc; i++)
31                 value = talloc_asprintf_append(value, " %s", argv[i]);
32
33         script_env_set(script, name, value);
34
35         return 0;
36 }
37
38 static int builtin_linux(struct grub2_script *script,
39                 void *data __attribute__((unused)),
40                 int argc, char *argv[])
41 {
42         struct discover_boot_option *opt = script->opt;
43         const char *root;
44         int i;
45
46         if (!opt) {
47                 pb_log("grub2 syntax error: 'linux' statement outside "
48                                 "a menuentry.\n");
49                 return -1;
50         }
51
52         if (argc < 2) {
53                 pb_log("grub2 syntax error: no filename provided to "
54                                 "linux statement\n");
55                 return -1;
56         }
57
58         root = script_env_get(script, "root");
59
60         opt->boot_image = create_grub2_resource(opt, script->ctx->device,
61                                                 root, argv[1]);
62         opt->option->boot_args = NULL;
63
64         if (argc > 2)
65                 opt->option->boot_args = talloc_strdup(opt, argv[2]);
66
67         for (i = 3; i < argc; i++)
68                 opt->option->boot_args = talloc_asprintf_append(
69                                                 opt->option->boot_args,
70                                                 " %s", argv[i]);
71         return 0;
72 }
73
74 static int builtin_initrd(struct grub2_script *script,
75                 void *data __attribute__((unused)),
76                 int argc, char *argv[])
77 {
78         struct discover_boot_option *opt = script->opt;
79         const char *root;
80
81         if (!opt) {
82                 pb_log("grub2 syntax error: 'initrd' statement outside "
83                                 "a menuentry.\n");
84                 return -1;
85         }
86
87         if (argc < 2) {
88                 pb_log("grub2 syntax error: no filename provided to "
89                                 "initrd statement\n");
90                 return -1;
91         }
92
93         root = script_env_get(script, "root");
94         opt->initrd = create_grub2_resource(opt, script->ctx->device,
95                                                 root, argv[1]);
96
97         return 0;
98 }
99
100 static int builtin_search(struct grub2_script *script,
101                 void *data __attribute__((unused)),
102                 int argc, char *argv[])
103 {
104         const char *env_var, *spec;
105         int i;
106
107         env_var = NULL;
108
109         for (i = 1; i < argc - 1; i++) {
110                 if (!strncmp(argv[i], "--set=", strlen("--set="))) {
111                         env_var = argv[i] + strlen("--set=");
112                         break;
113                 }
114         }
115
116         if (!env_var)
117                 return 0;
118
119         spec = argv[argc - 1];
120
121         script_env_set(script, env_var, spec);
122
123         return 0;
124 }
125
126 static bool builtin_test_op(int argc, char **argv, int *consumed)
127 {
128         char *op;
129
130         if (argc >= 3) {
131                 const char *a1, *a2;
132
133                 a1 = argv[0];
134                 op = argv[1];
135                 a2 = argv[2];
136
137                 if (!strcmp(op, "=") || !strcmp(op, "==")) {
138                         *consumed = 3;
139                         return !strcmp(a1, a2);
140                 }
141
142                 if (!strcmp(op, "!=")) {
143                         *consumed = 3;
144                         return strcmp(a1, a2);
145                 }
146
147                 if (!strcmp(op, "<")) {
148                         *consumed = 3;
149                         return strcmp(a1, a2) < 0;
150                 }
151
152                 if (!strcmp(op, ">")) {
153                         *consumed = 3;
154                         return strcmp(a1, a2) > 0;
155                 }
156         }
157
158         if (argc >= 2) {
159                 const char *a1;
160
161                 op = argv[0];
162                 a1 = argv[1];
163
164                 if (!strcmp(op, "-z")) {
165                         *consumed = 2;
166                         return strlen(a1) == 0;
167                 }
168
169                 if (!strcmp(op, "-n")) {
170                         *consumed = 2;
171                         return strlen(a1) != 0;
172                 }
173
174                 /* todo: implement file checks */
175                 if (!strcmp(op, "-s") || !strcmp(op, "-f")) {
176                         *consumed = 2;
177                         return false;
178                 }
179         }
180
181         op = argv[0];
182         *consumed = 1;
183         return strlen(op) > 0;
184 }
185
186 static int builtin_test(struct grub2_script *script __attribute__((unused)),
187                 void *data __attribute__((unused)),
188                 int argc, char *argv[])
189 {
190         int consumed;
191         bool not, rc;
192
193         if (!strcmp(argv[0], "[") && !strcmp(argv[argc - 1], "]"))
194                 argc--;
195
196         /* skip command name */
197         argc--;
198         argv++;
199
200         not = false;
201         rc = false;
202
203         for (consumed = 0; argc > 0; argv += consumed, argc -= consumed) {
204
205                 if (!strcmp(argv[0], "!")) {
206                         not = true;
207                         consumed = 1;
208                         continue;
209                 }
210
211                 if (!strcmp(argv[0], "-a")) {
212                         if (!rc)
213                                 return 1;
214                         consumed = 1;
215                         continue;
216                 }
217
218                 if (!strcmp(argv[0], "-o")) {
219                         if (rc)
220                                 return 0;
221                         consumed = 1;
222                         continue;
223                 }
224
225                 rc = builtin_test_op(argc, argv, &consumed);
226                 if (not) {
227                         rc = !rc;
228                         not = false;
229                 }
230         }
231
232         return rc ? 0 : 1;
233 }
234
235 static int builtin_true(struct grub2_script *script __attribute__((unused)),
236                 void *data __attribute__((unused)),
237                 int argc __attribute__((unused)),
238                 char *argv[] __attribute__((unused)))
239 {
240         return 0;
241 }
242
243 static int builtin_false(struct grub2_script *script __attribute__((unused)),
244                 void *data __attribute__((unused)),
245                 int argc __attribute__((unused)),
246                 char *argv[] __attribute__((unused)))
247 {
248         return 1;
249 }
250
251 static int builtin_nop(struct grub2_script *script __attribute__((unused)),
252                 void *data __attribute__((unused)),
253                 int argc __attribute__((unused)),
254                 char *argv[] __attribute__((unused)))
255 {
256         return 0;
257 }
258
259 extern int builtin_load_env(struct grub2_script *script,
260                 void *data __attribute__((unused)),
261                 int argc, char *argv[]);
262 int builtin_save_env(struct grub2_script *script,
263                 void *data __attribute__((unused)),
264                 int argc, char *argv[]);
265
266
267 static struct {
268         const char *name;
269         grub2_function fn;
270 } builtins[] = {
271         {
272                 .name = "set",
273                 .fn = builtin_set,
274         },
275         {
276                 .name = "linux",
277                 .fn = builtin_linux,
278         },
279         {
280                 .name = "linux16",
281                 .fn = builtin_linux,
282         },
283         {
284                 .name = "initrd",
285                 .fn = builtin_initrd,
286         },
287         {
288                 .name = "search",
289                 .fn = builtin_search,
290         },
291         {
292                 .name = "[",
293                 .fn = builtin_test,
294         },
295         {
296                 .name = "test",
297                 .fn = builtin_test,
298         },
299         {
300                 .name = "true",
301                 .fn = builtin_true,
302         },
303         {
304                 .name = "false",
305                 .fn = builtin_false,
306         },
307         {
308                 .name = "load_env",
309                 .fn = builtin_load_env,
310         },
311         {
312                 .name = "save_env",
313                 .fn = builtin_save_env,
314         },
315 };
316
317 static const char *nops[] = {
318         "echo", "export", "insmod", "loadfont", "terminfo",
319 };
320
321 void register_builtins(struct grub2_script *script)
322 {
323         unsigned int i;
324
325         for (i = 0; i < ARRAY_SIZE(builtins); i++)
326                 script_register_function(script, builtins[i].name,
327                                 builtins[i].fn, NULL);
328
329         for (i = 0; i < ARRAY_SIZE(nops); i++)
330                 script_register_function(script, nops[i], builtin_nop, NULL);
331 }