loader(8): Add back some loader.conf vars which the old code handled.
[dragonfly.git] / sys / boot / dloader / cmds.c
1 /*
2  * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <stand.h>
36 #include <string.h>
37 #include "bootstrap.h"
38 #include "dloader.h"
39
40 static void menu_display(void);
41 static int menu_execute(int);
42
43 /*
44  * This is called from common and must reference files to bring
45  * library modules into common during linking.
46  */
47 void
48 dloader_init_cmds(void)
49 {
50 }
51
52 /*
53  * This intercepts lines of the form 'a=b'
54  */
55 COMMAND_SET(local, "local", "List local variables", command_local);
56 COMMAND_SET(lunset, "lunset", "Unset local variables", command_lunset);
57 COMMAND_SET(lunsetif, "lunsetif", "Unset local if envvar set to 1 or YES", command_lunsetif);
58 COMMAND_SET(loadall, "loadall", "Load kernel + modules", command_loadall);
59 COMMAND_SET(menuclear, "menuclear", "Clear all menus", command_menuclear);
60 COMMAND_SET(menuitem, "menuitem", "Add menu bullet", command_menuitem);
61 COMMAND_SET(menuadd, "menuadd", "Add script line for bullet", command_menuadd);
62 COMMAND_SET(menu, "menu", "Run menu system", command_menu);
63
64 static int curitem;
65 static int curadd;
66
67 static char *kenv_vars[] = {
68         "boot_askname",
69         "boot_cdrom",
70         "boot_ddb",
71         "boot_gdb",
72         "boot_serial",
73         "boot_single",
74         "boot_userconfig",
75         "boot_verbose",
76         "boot_vidcons",
77         "bootfile",
78         "console",
79         "currdev",
80         "kernelname",
81         "loaddev",
82         "module_path",
83         NULL
84 };
85
86 /*
87  * Set local variable.  Sniff "module_path"
88  *
89  * format a=b (one argument) - in av[0]
90  */
91 static int
92 command_local(int ac, char **av)
93 {
94         char *name;
95         char *data;
96         dvar_t dvar;
97         int i;
98         int j;
99
100         /*
101          * local command executed directly.
102          */
103         if (strcmp(av[0], "local") == 0) {
104                 for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
105                         for (j = 1; j < ac; ++j) {
106                                 if (!strncmp(dvar->name, av[j], strlen(av[j])))
107                                         break;
108                         }
109                         if (ac > 1 && j == ac)
110                                 continue;
111
112                         printf("%s=", dvar->name);
113                         for (i = 0; i < dvar->count; ++i) {
114                                 if (i)
115                                         printf(",");
116                                 printf("\"%s\"", dvar->data[i]);
117                         }
118                         printf("\n");
119                 }
120                 return(CMD_OK);
121         }
122
123         /*
124          * local command intercept for blah=blah
125          */
126         name = av[0];
127         data = strchr(name, '=');
128         if (data == NULL) {
129                 sprintf(command_errbuf, "Bad variable syntax");
130                 return (CMD_ERROR);
131         }
132         *data++ = 0;
133
134         if (*data)
135                 dvar_set(name, &data, 1);
136         else
137                 dvar_unset(name);
138
139         /*
140          * Take care of loader tunables and several other variables,
141          * all of which have to mirror to kenv because libstand or
142          * other consumers may have hooks into them.
143          */
144         if (strchr(name, '.')) {
145                 setenv(name, data, 1);
146         } else {
147                 for (i = 0; kenv_vars[i] != NULL; i++) {
148                         if (strcmp(name, kenv_vars[i]) == 0) {
149                                 setenv(name, data, 1);
150                                 return(CMD_OK);
151                         }
152                 }
153         }
154         return(CMD_OK);
155 }
156
157 /*
158  * Unset local variables
159  */
160 static int
161 command_lunset(int ac, char **av)
162 {
163         int i;
164
165         for (i = 1; i < ac; ++i)
166                 dvar_unset(av[i]);
167         return(0);
168 }
169
170 static int
171 command_lunsetif(int ac, char **av)
172 {
173         char *envdata;
174
175         if (ac != 3) {
176                 sprintf(command_errbuf,
177                         "syntax error use lunsetif lname envname");
178                 return(CMD_ERROR);
179         }
180         envdata = getenv(av[2]);
181         if (strcmp(envdata, "yes") == 0 ||
182             strcmp(envdata, "YES") == 0 ||
183             strtol(envdata, NULL, 0)) {
184                 dvar_unset(av[1]);
185         }
186         return (CMD_OK);
187 }
188
189 /*
190  * Load the kernel + all modules specified with
191  */
192 static int
193 command_loadall(int ac, char **av)
194 {
195         char *argv[4];
196         dvar_t dvar;
197         int len;
198         int argc;
199         int res;
200         int tmp;
201
202         argv[0] = "unload";
203         (void)perform(1, argv);
204
205         /*
206          * Load kernel
207          */
208         argv[0] = "load";
209         argv[1] = getenv("kernelname");
210         if (argv[1] == NULL)
211                 argv[1] = strdup("kernel");
212         res = perform(2, argv);
213         free(argv[1]);
214
215         if (res != CMD_OK) {
216                 printf("Unable to load %s%s\n", DirBase, argv[1]);
217                 return(res);
218         }
219
220         /*
221          * Load modules
222          */
223         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
224                 len = strlen(dvar->name);
225                 if (len <= 5 || strcmp(dvar->name + len - 5, "_load"))
226                         continue;
227                 if (strcmp(dvar->data[0], "yes") != 0 &&
228                     strcmp(dvar->data[0], "YES") != 0) {
229                         continue;
230                 }
231                 argv[0] = "load";
232                 argv[1] = strdup(dvar->name);
233                 argv[1][len - 5] = 0;
234                 tmp = perform(2, argv);
235                 free(argv[1]);
236                 if (tmp != CMD_OK) {
237                         time_t t = time(NULL);
238                         printf("Unable to load %s%s\n", DirBase, argv[1]);
239                         while (time(NULL) == t)
240                                 ;
241                         /* don't kill the boot sequence */
242                         /* res = tmp; */
243                 }
244
245         }
246         return(res);
247 }
248
249 /*
250  * Clear all menus
251  */
252 static int
253 command_menuclear(int ac, char **av)
254 {
255         dvar_unset("menu_*");
256         dvar_unset("item_*");
257         curitem = 0;
258         curadd = 0;
259         return(0);
260 }
261
262 /*
263  * Add menu bullet
264  */
265 static int
266 command_menuitem(int ac, char **av)
267 {
268         char namebuf[32];
269         char *cp;
270
271         if (ac != 3) {
272                 sprintf(command_errbuf, "Bad menuitem syntax");
273                 return (CMD_ERROR);
274         }
275         curitem = (unsigned char)av[1][0];
276         if (curitem == 0) {
277                 sprintf(command_errbuf, "Bad menuitem syntax");
278                 return (CMD_ERROR);
279         }
280         snprintf(namebuf, sizeof(namebuf), "menu_%c", curitem);
281         dvar_set(namebuf, &av[2], 1);
282         curadd = 0;
283
284         return(CMD_OK);
285 }
286
287 /*
288  * Add execution item
289  */
290 static int
291 command_menuadd(int ac, char **av)
292 {
293         char namebuf[32];
294         int i;
295
296         if (ac == 1)
297                 return(CMD_OK);
298         if (curitem == 0) {
299                 sprintf(command_errbuf, "Missing menuitem for menuadd");
300                 return(CMD_ERROR);
301         }
302         snprintf(namebuf, sizeof(namebuf), "item_%c_%d", curitem, curadd);
303         dvar_set(namebuf, &av[1], ac - 1);
304         ++curadd;
305         return (CMD_OK);
306 }
307
308 /*
309  * Execute menu system
310  */
311 static int
312 command_menu(int ac, char **av)
313 {
314         int timeout = -1;
315         time_t time_target;
316         time_t time_last;
317         time_t t;
318         char *cp;
319         int c;
320         int res;
321         int counting = 1;
322
323         menu_display();
324         if ((cp = getenv("autoboot_delay")) != NULL)
325                 timeout = strtol(cp, NULL, 0);
326         if (timeout <= 0)
327                 timeout = 10;
328         if (timeout > 24 * 60 * 60)
329                 timeout = 24 * 60 * 60;
330
331         time_target = time(NULL) + timeout;
332         time_last = 0;
333         c = '1';
334         for (;;) {
335                 if (ischar()) {
336                         c = getchar();
337                         if (c == '\r' || c == '\n') {
338                                 c = '1';
339                                 break;
340                         }
341                         if (c == ' ') {
342                                 if (counting) {
343                                         printf("\rCountdown halted by "
344                                                "space   ");
345                                 }
346                                 counting = 0;
347                                 continue;
348                         }
349                         if (c == 0x1b) {
350                                 setenv("autoboot_delay", "NO", 1);
351                                 return(CMD_OK);
352                         }
353                         res = menu_execute(c);
354                         if (res >= 0) {
355                                 setenv("autoboot_delay", "NO", 1);
356                                 return(CMD_OK);
357                         }
358                         /* else ignore char */
359                 }
360                 if (counting) {
361                         t = time(NULL);
362                         if (time_last == t)
363                                 continue;
364                         time_last = t;
365                         printf("\rBooting in %d second%s... ",
366                                 (int)(time_target - t),
367                                 ((time_target - t) == 1 ? "" : "s"));
368                         if ((int)(time_target - t) <= 0) {
369                                 c = '1';
370                                 break;
371                         }
372                 }
373         }
374         res = menu_execute(c);
375         if (res != CMD_OK)
376                 setenv("autoboot_delay", "NO", 1);
377         return (res);
378 }
379
380 static void
381 menu_display(void)
382 {
383         dvar_t dvar;
384
385         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
386                 if (strncmp(dvar->name, "menu_", 5) == 0) {
387                         printf("%c. %s\n", dvar->name[5], dvar->data[0]);
388                 }
389         }
390         printf("\n");
391 }
392
393 static int
394 menu_execute(int c)
395 {
396         dvar_t dvar;
397         char namebuf[32];
398         int res;
399
400         printf("\n");
401
402         snprintf(namebuf, sizeof(namebuf), "item_%c_0", c);
403
404         /*
405          * Does this menu option exist?
406          */
407         if (dvar_get(namebuf) == NULL)
408                 return(-1);
409
410         snprintf(namebuf, sizeof(namebuf), "item_%c", c);
411         res = CMD_OK;
412         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
413                 if (strncmp(dvar->name, namebuf, 6) == 0) {
414                         res = perform(dvar->count, dvar->data);
415                         if (res != CMD_OK) {
416                                 printf("%s: %s\n",
417                                         dvar->data[0], command_errmsg);
418                                 setenv("autoboot_delay", "NO", 1);
419                                 break;
420                         }
421                 }
422         }
423         return(res);
424 }