loader(8): Make the init_path setting work again.
[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         "init_path",
81         "kernelname",
82         "loaddev",
83         "module_path",
84         NULL
85 };
86
87 /*
88  * Set local variable.  Sniff "module_path"
89  *
90  * format a=b (one argument) - in av[0]
91  */
92 static int
93 command_local(int ac, char **av)
94 {
95         char *name;
96         char *data;
97         dvar_t dvar;
98         int i;
99         int j;
100
101         /*
102          * local command executed directly.
103          */
104         if (strcmp(av[0], "local") == 0) {
105                 for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
106                         for (j = 1; j < ac; ++j) {
107                                 if (!strncmp(dvar->name, av[j], strlen(av[j])))
108                                         break;
109                         }
110                         if (ac > 1 && j == ac)
111                                 continue;
112
113                         printf("%s=", dvar->name);
114                         for (i = 0; i < dvar->count; ++i) {
115                                 if (i)
116                                         printf(",");
117                                 printf("\"%s\"", dvar->data[i]);
118                         }
119                         printf("\n");
120                 }
121                 return(CMD_OK);
122         }
123
124         /*
125          * local command intercept for blah=blah
126          */
127         name = av[0];
128         data = strchr(name, '=');
129         if (data == NULL) {
130                 sprintf(command_errbuf, "Bad variable syntax");
131                 return (CMD_ERROR);
132         }
133         *data++ = 0;
134
135         if (*data)
136                 dvar_set(name, &data, 1);
137         else
138                 dvar_unset(name);
139
140         /*
141          * Take care of loader tunables and several other variables,
142          * all of which have to mirror to kenv because libstand or
143          * other consumers may have hooks into them.
144          */
145         if (strchr(name, '.')) {
146                 setenv(name, data, 1);
147         } else {
148                 for (i = 0; kenv_vars[i] != NULL; i++) {
149                         if (strcmp(name, kenv_vars[i]) == 0) {
150                                 setenv(name, data, 1);
151                                 return(CMD_OK);
152                         }
153                 }
154         }
155         return(CMD_OK);
156 }
157
158 /*
159  * Unset local variables
160  */
161 static int
162 command_lunset(int ac, char **av)
163 {
164         int i;
165
166         for (i = 1; i < ac; ++i)
167                 dvar_unset(av[i]);
168         return(0);
169 }
170
171 static int
172 command_lunsetif(int ac, char **av)
173 {
174         char *envdata;
175
176         if (ac != 3) {
177                 sprintf(command_errbuf,
178                         "syntax error use lunsetif lname envname");
179                 return(CMD_ERROR);
180         }
181         envdata = getenv(av[2]);
182         if (strcmp(envdata, "yes") == 0 ||
183             strcmp(envdata, "YES") == 0 ||
184             strtol(envdata, NULL, 0)) {
185                 dvar_unset(av[1]);
186         }
187         return (CMD_OK);
188 }
189
190 /*
191  * Load the kernel + all modules specified with
192  */
193 static int
194 command_loadall(int ac, char **av)
195 {
196         char *argv[4];
197         dvar_t dvar;
198         int len;
199         int argc;
200         int res;
201         int tmp;
202
203         argv[0] = "unload";
204         (void)perform(1, argv);
205
206         /*
207          * Load kernel
208          */
209         argv[0] = "load";
210         argv[1] = getenv("kernelname");
211         if (argv[1] == NULL)
212                 argv[1] = strdup("kernel");
213         res = perform(2, argv);
214         free(argv[1]);
215
216         if (res != CMD_OK) {
217                 printf("Unable to load %s%s\n", DirBase, argv[1]);
218                 return(res);
219         }
220
221         /*
222          * Load modules
223          */
224         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
225                 len = strlen(dvar->name);
226                 if (len <= 5 || strcmp(dvar->name + len - 5, "_load"))
227                         continue;
228                 if (strcmp(dvar->data[0], "yes") != 0 &&
229                     strcmp(dvar->data[0], "YES") != 0) {
230                         continue;
231                 }
232                 argv[0] = "load";
233                 argv[1] = strdup(dvar->name);
234                 argv[1][len - 5] = 0;
235                 tmp = perform(2, argv);
236                 free(argv[1]);
237                 if (tmp != CMD_OK) {
238                         time_t t = time(NULL);
239                         printf("Unable to load %s%s\n", DirBase, argv[1]);
240                         while (time(NULL) == t)
241                                 ;
242                         /* don't kill the boot sequence */
243                         /* res = tmp; */
244                 }
245
246         }
247         return(res);
248 }
249
250 /*
251  * Clear all menus
252  */
253 static int
254 command_menuclear(int ac, char **av)
255 {
256         dvar_unset("menu_*");
257         dvar_unset("item_*");
258         curitem = 0;
259         curadd = 0;
260         return(0);
261 }
262
263 /*
264  * Add menu bullet
265  */
266 static int
267 command_menuitem(int ac, char **av)
268 {
269         char namebuf[32];
270         char *cp;
271
272         if (ac != 3) {
273                 sprintf(command_errbuf, "Bad menuitem syntax");
274                 return (CMD_ERROR);
275         }
276         curitem = (unsigned char)av[1][0];
277         if (curitem == 0) {
278                 sprintf(command_errbuf, "Bad menuitem syntax");
279                 return (CMD_ERROR);
280         }
281         snprintf(namebuf, sizeof(namebuf), "menu_%c", curitem);
282         dvar_set(namebuf, &av[2], 1);
283         curadd = 0;
284
285         return(CMD_OK);
286 }
287
288 /*
289  * Add execution item
290  */
291 static int
292 command_menuadd(int ac, char **av)
293 {
294         char namebuf[32];
295         int i;
296
297         if (ac == 1)
298                 return(CMD_OK);
299         if (curitem == 0) {
300                 sprintf(command_errbuf, "Missing menuitem for menuadd");
301                 return(CMD_ERROR);
302         }
303         snprintf(namebuf, sizeof(namebuf), "item_%c_%d", curitem, curadd);
304         dvar_set(namebuf, &av[1], ac - 1);
305         ++curadd;
306         return (CMD_OK);
307 }
308
309 /*
310  * Execute menu system
311  */
312 static int
313 command_menu(int ac, char **av)
314 {
315         int timeout = -1;
316         time_t time_target;
317         time_t time_last;
318         time_t t;
319         char *cp;
320         int c;
321         int res;
322         int counting = 1;
323
324         menu_display();
325         if ((cp = getenv("autoboot_delay")) != NULL)
326                 timeout = strtol(cp, NULL, 0);
327         if (timeout <= 0)
328                 timeout = 10;
329         if (timeout > 24 * 60 * 60)
330                 timeout = 24 * 60 * 60;
331
332         time_target = time(NULL) + timeout;
333         time_last = 0;
334         c = '1';
335         for (;;) {
336                 if (ischar()) {
337                         c = getchar();
338                         if (c == '\r' || c == '\n') {
339                                 c = '1';
340                                 break;
341                         }
342                         if (c == ' ') {
343                                 if (counting) {
344                                         printf("\rCountdown halted by "
345                                                "space   ");
346                                 }
347                                 counting = 0;
348                                 continue;
349                         }
350                         if (c == 0x1b) {
351                                 setenv("autoboot_delay", "NO", 1);
352                                 return(CMD_OK);
353                         }
354                         res = menu_execute(c);
355                         if (res >= 0) {
356                                 setenv("autoboot_delay", "NO", 1);
357                                 return(CMD_OK);
358                         }
359                         /* else ignore char */
360                 }
361                 if (counting) {
362                         t = time(NULL);
363                         if (time_last == t)
364                                 continue;
365                         time_last = t;
366                         printf("\rBooting in %d second%s... ",
367                                 (int)(time_target - t),
368                                 ((time_target - t) == 1 ? "" : "s"));
369                         if ((int)(time_target - t) <= 0) {
370                                 c = '1';
371                                 break;
372                         }
373                 }
374         }
375         res = menu_execute(c);
376         if (res != CMD_OK)
377                 setenv("autoboot_delay", "NO", 1);
378         return (res);
379 }
380
381 static void
382 menu_display(void)
383 {
384         dvar_t dvar;
385
386         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
387                 if (strncmp(dvar->name, "menu_", 5) == 0) {
388                         printf("%c. %s\n", dvar->name[5], dvar->data[0]);
389                 }
390         }
391         printf("\n");
392 }
393
394 static int
395 menu_execute(int c)
396 {
397         dvar_t dvar;
398         char namebuf[32];
399         int res;
400
401         printf("\n");
402
403         snprintf(namebuf, sizeof(namebuf), "item_%c_0", c);
404
405         /*
406          * Does this menu option exist?
407          */
408         if (dvar_get(namebuf) == NULL)
409                 return(-1);
410
411         snprintf(namebuf, sizeof(namebuf), "item_%c", c);
412         res = CMD_OK;
413         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
414                 if (strncmp(dvar->name, namebuf, 6) == 0) {
415                         res = perform(dvar->count, dvar->data);
416                         if (res != CMD_OK) {
417                                 printf("%s: %s\n",
418                                         dvar->data[0], command_errmsg);
419                                 setenv("autoboot_delay", "NO", 1);
420                                 break;
421                         }
422                 }
423         }
424         return(res);
425 }