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