xref: /dragonfly/stand/boot/dloader/cmds.c (revision 7d3e9a5b)
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  * "local" intercepts assignments: lines of the form 'a=b'
54  */
55 COMMAND_SET(local, "local", "List local variables", command_local);
56 COMMAND_SET(lunset, "lunset", "Unset local variable", command_lunset);
57 COMMAND_SET(lunsetif, "lunsetif", "Unset local variable if kenv variable is true", 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 	"LINES",
69 	"acpi_load",
70 	"autoboot_delay",
71 	"boot_askname",
72 	"boot_cdrom",
73 	"boot_ddb",
74 	"boot_gdb",
75 	"boot_serial",
76 	"boot_single",
77 	"boot_verbose",
78 	"boot_vidcons",
79 	"bootfile",
80 	"console",
81 	"currdev",
82 	"default_kernel",
83 	"dumpdev",
84 	"ehci_load",
85 	"interpret",
86 	"init_chroot",
87 	"init_path",
88 	"kernel_options",
89 	"kernelname",
90 	"loaddev",
91 	"local_modules",
92 	"module_path",
93 	"num_ide_disks",
94 	"prompt",
95 	"rootdev",
96 	"root_disk_unit",
97 	"xhci_load",
98 	NULL
99 };
100 
101 /*
102  * List or set local variable.  Sniff assignment of kenv_vars[] and
103  * loader tunables (recognized by '.' in name).
104  *
105  * format for av[0]:
106  *  - List: local
107  *  - Set:  var=val
108  */
109 static int
110 command_local(int ac, char **av)
111 {
112 	char *name;
113 	char *data;
114 	dvar_t dvar;
115 	int i;
116 	int j;
117 
118 	/*
119 	 * local command executed directly.
120 	 */
121 	if (strcmp(av[0], "local") == 0) {
122 		pager_open();
123 		for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
124 			for (j = 1; j < ac; ++j) {
125 				if (!strncmp(dvar->name, av[j], strlen(av[j])))
126 					break;
127 			}
128 			if (ac > 1 && j == ac)
129 				continue;
130 
131 			pager_output(dvar->name);
132 			pager_output("=");
133 			for (i = 0; i < dvar->count; ++i) {
134 				if (i)
135 					pager_output(",");
136 				pager_output("\"");
137 				pager_output(dvar->data[i]);
138 				pager_output("\"");
139 			}
140 			pager_output("\n");
141 		}
142 		pager_close();
143 		return(CMD_OK);
144 	}
145 
146 	/*
147 	 * local command intercept for 'var=val'
148 	 */
149 	name = av[0];
150 	data = strchr(name, '=');
151 	if (data == NULL) {
152 		sprintf(command_errbuf, "Bad variable syntax");
153 		return (CMD_ERROR);
154 	}
155 	*data++ = 0;
156 
157 	if (*data)
158 		dvar_set(name, &data, 1);
159 	else
160 		dvar_unset(name);
161 
162 	/*
163 	 * Take care of loader tunables and several other variables,
164 	 * all of which have to mirror to kenv because libstand or
165 	 * other consumers may have hooks into them.
166 	 */
167 	if (strchr(name, '.')) {
168 		setenv(name, data, 1);
169 	} else {
170 		for (i = 0; kenv_vars[i] != NULL; i++) {
171 			if (strcmp(name, kenv_vars[i]) == 0) {
172 				setenv(name, data, 1);
173 				return(CMD_OK);
174 			}
175 		}
176 	}
177 	return(CMD_OK);
178 }
179 
180 /*
181  * Unset local variables
182  */
183 static int
184 command_lunset(int ac, char **av)
185 {
186 	int i;
187 
188 	for (i = 1; i < ac; ++i)
189 		dvar_unset(av[i]);
190 	return(0);
191 }
192 
193 static int
194 command_lunsetif(int ac, char **av)
195 {
196 	char *envdata;
197 
198 	if (ac != 3) {
199 		sprintf(command_errbuf,
200 			"syntax error use lunsetif lname envname");
201 		return(CMD_ERROR);
202 	}
203 	envdata = getenv(av[2]);
204 	if (strcmp(envdata, "yes") == 0 ||
205 	    strcmp(envdata, "YES") == 0 ||
206 	    strtol(envdata, NULL, 0)) {
207 		dvar_unset(av[1]);
208 	}
209 	return (CMD_OK);
210 }
211 
212 /*
213  * Load the kernel + all modules specified with MODULE_load="YES"
214  */
215 static int
216 command_loadall(int ac, char **av)
217 {
218 	char *argv[4];
219 	char *mod_name;
220 	char *mod_fname;
221 	char *mod_type;
222 	char *tmp_str;
223 	dvar_t dvar, dvar2;
224 	int len;
225 	int argc;
226 	int res;
227 	int tmp;
228 
229 	argv[0] = "unload";
230 	(void)perform(1, argv);
231 
232 	/*
233 	 * Load kernel
234 	 */
235 	argv[0] = "load";
236 	argv[1] = getenv("kernelname");
237 	argv[2] = getenv("kernel_options");
238 	if (argv[1] == NULL)
239 		argv[1] = strdup("kernel");
240 	res = perform((argv[2] == NULL)?2:3, argv);
241 	free(argv[1]);
242 	if (argv[2])
243 		free(argv[2]);
244 
245 	if (res != CMD_OK) {
246 		printf("Unable to load %s%s\n", DirBase, argv[1]);
247 		return(res);
248 	}
249 
250 	/*
251 	 * Load modules
252 	 */
253 	for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
254 		len = strlen(dvar->name);
255 		if (len <= 5 || strcmp(dvar->name + len - 5, "_load"))
256 			continue;
257 		if (strcmp(dvar->data[0], "yes") != 0 &&
258 		    strcmp(dvar->data[0], "YES") != 0) {
259 			continue;
260 		}
261 
262 		mod_name = strdup(dvar->name);
263 		mod_name[len - 5] = 0;
264 		mod_type = NULL;
265 		mod_fname = NULL;
266 
267 		/* Check if there's a matching foo_type */
268 		for (dvar2 = dvar_first();
269 		     dvar2 && (mod_type == NULL);
270 		     dvar2 = dvar_next(dvar2)) {
271 			len = strlen(dvar2->name);
272 			if (len <= 5 || strcmp(dvar2->name + len - 5, "_type"))
273 				continue;
274 			tmp_str = strdup(dvar2->name);
275 			tmp_str[len - 5] = 0;
276 			if (strcmp(tmp_str, mod_name) == 0)
277 				mod_type = dvar2->data[0];
278 
279 			free(tmp_str);
280 		}
281 
282 		/* Check if there's a matching foo_name */
283 		for (dvar2 = dvar_first();
284 		     dvar2 && (mod_fname == NULL);
285 		     dvar2 = dvar_next(dvar2)) {
286 			len = strlen(dvar2->name);
287 			if (len <= 5 || strcmp(dvar2->name + len - 5, "_name"))
288 				continue;
289 			tmp_str = strdup(dvar2->name);
290 			tmp_str[len - 5] = 0;
291 			if (strcmp(tmp_str, mod_name) == 0) {
292 				mod_fname = dvar2->data[0];
293 				free(mod_name);
294 				mod_name = strdup(mod_fname);
295 			}
296 
297 			free(tmp_str);
298 		}
299 
300 		argv[0] = "load";
301 		if (mod_type) {
302 			argc = 4;
303 			argv[1] = "-t";
304 			argv[2] = mod_type;
305 			argv[3] = mod_name;
306 		} else {
307 			argc = 2;
308 			argv[1] = mod_name;
309 		}
310 		tmp = perform(argc, argv);
311 		if (tmp != CMD_OK) {
312 			time_t t = time(NULL);
313 			printf("Unable to load %s%s\n", DirBase, mod_name);
314 			while (time(NULL) == t)
315 				;
316 			/* don't kill the boot sequence */
317 			/* res = tmp; */
318 		}
319 		free(mod_name);
320 	}
321 	return(res);
322 }
323 
324 /*
325  * Clear all menus
326  */
327 static int
328 command_menuclear(int ac, char **av)
329 {
330 	dvar_unset("menu_*");
331 	dvar_unset("item_*");
332 	curitem = 0;
333 	curadd = 0;
334 	return(0);
335 }
336 
337 /*
338  * Add menu bullet
339  */
340 static int
341 command_menuitem(int ac, char **av)
342 {
343 	char namebuf[32];
344 
345 	if (ac != 3) {
346 		sprintf(command_errbuf, "Bad menuitem syntax");
347 		return (CMD_ERROR);
348 	}
349 	curitem = (unsigned char)av[1][0];
350 	if (curitem == 0) {
351 		sprintf(command_errbuf, "Bad menuitem syntax");
352 		return (CMD_ERROR);
353 	}
354 	snprintf(namebuf, sizeof(namebuf), "menu_%c", curitem);
355 	dvar_set(namebuf, &av[2], 1);
356 	curadd = 0;
357 
358 	return(CMD_OK);
359 }
360 
361 /*
362  * Add execution item
363  */
364 static int
365 command_menuadd(int ac, char **av)
366 {
367 	char namebuf[32];
368 
369 	if (ac == 1)
370 		return(CMD_OK);
371 	if (curitem == 0) {
372 		sprintf(command_errbuf, "Missing menuitem for menuadd");
373 		return(CMD_ERROR);
374 	}
375 	snprintf(namebuf, sizeof(namebuf), "item_%c_%d", curitem, curadd);
376 	dvar_set(namebuf, &av[1], ac - 1);
377 	++curadd;
378 	return (CMD_OK);
379 }
380 
381 /*
382  * Execute menu system
383  */
384 static int
385 command_menu(int ac, char **av)
386 {
387 	int timeout = -1;
388 	time_t time_target;
389 	time_t time_last;
390 	time_t t;
391 	char *cp;
392 	int c;
393 	int res;
394 	int counting = 1;
395 
396 	menu_display();
397 	if ((cp = getenv("autoboot_delay")) != NULL)
398 		timeout = strtol(cp, NULL, 0);
399 	if (timeout <= 0)
400 		timeout = 10;
401 	if (timeout > 24 * 60 * 60)
402 		timeout = 24 * 60 * 60;
403 
404 	time_target = time(NULL) + timeout;
405 	time_last = 0;
406 	c = '1';
407 	for (;;) {
408 		if (ischar()) {
409 			c = getchar();
410 			if (c == '\r' || c == '\n') {
411 				c = '1';
412 				break;
413 			}
414 			if (c == ' ') {
415 				if (counting) {
416 					printf("\rCountdown halted by "
417 					       "space   ");
418 				}
419 				counting = 0;
420 				continue;
421 			}
422 			if (c == 0x1b) {
423 				setenv("autoboot_delay", "NO", 1);
424 				return(CMD_OK);
425 			}
426 			res = menu_execute(c);
427 			if (res >= 0) {
428 				setenv("autoboot_delay", "NO", 1);
429 				return(CMD_OK);
430 			}
431 			/* else ignore char */
432 		}
433 		if (counting) {
434 			t = time(NULL);
435 			if (time_last == t)
436 				continue;
437 			time_last = t;
438 			printf("\rBooting in %d second%s... ",
439 				(int)(time_target - t),
440 				((time_target - t) == 1 ? "" : "s"));
441 			if ((int)(time_target - t) <= 0) {
442 				c = '1';
443 				break;
444 			}
445 		}
446 	}
447 	res = menu_execute(c);
448 	if (res != CMD_OK)
449 		setenv("autoboot_delay", "NO", 1);
450 	return (res);
451 }
452 
453 #define LOGO_LINES 16
454 #define FRED_LEFT 0
455 #define FRED_RIGHT 1
456 static char *logo_blank_line = "                                 ";
457 
458 static char *logo_color[LOGO_LINES] = {
459 	" ,--,                       ,--, ",
460 	" |   `-,       _:_       ,-'   | ",
461 	"  `,    `-,   (/ \\)   ,-'    ,'  ",
462 	"    `-,    `-,/   \\,-'    ,-'    ",
463 	"       `------{   }------'       ",
464 	"   ,----------{   }----------,   ",
465 	"  |        _,-{   }-,_        |  ",
466 	"   `-,__,-'   \\   /   `-,__,-'   ",
467 	"               | |               ",
468 	"               | |               ",
469 	"               | |               ",
470 	"               | |               ",
471 	"               | |               ",
472 	"               | |               ",
473 	"               `,'               ",
474 	"                                 " };
475 
476 static char *logo_indigo[LOGO_LINES] = {
477 	" ,--,                       ,--, ",
478 	" |   `-,       _:_       ,-'   | ",
479 	"  `,    `-,   (/ \\)   ,-'    ,'  ",
480 	"    `-,    `-,/   \\,-'    ,-'    ",
481 	"       `------{   }------'       ",
482 	"   ,----------{   }----------,   ",
483 	"  |        _,-{   }-,_        |  ",
484 	"   `-,__,-'   \\   /   `-,__,-'   ",
485 	"               | |               ",
486 	"               | |               ",
487 	"               | |               ",
488 	"               | |               ",
489 	"               | |               ",
490 	"               | |               ",
491 	"               `,'               ",
492 	"                                 " };
493 
494 static char *logo_mono[LOGO_LINES] =  {
495 	" ,--,                       ,--, ",
496 	" |   `-,       _:_       ,-'   | ",
497 	"  `,    `-,   (/ \\)   ,-'    ,'  ",
498 	"    `-,    `-,/   \\,-'    ,-'    ",
499 	"       `------{   }------'       ",
500 	"   ,----------{   }----------,   ",
501 	"  |        _,-{   }-,_        |  ",
502 	"   `-,__,-'   \\   /   `-,__,-'   ",
503 	"               | |               ",
504 	"               | |               ",
505 	"               | |               ",
506 	"               | |               ",
507 	"               | |               ",
508 	"               | |               ",
509 	"               `,'               ",
510 	"                                 " };
511 
512 static void
513 logo_display(char **logo, int line, int orientation, int barrier)
514 {
515 	const char *fmt;
516 
517 	if (orientation == FRED_LEFT)
518 		fmt = barrier ? "%s  | " : "  %s  ";
519 	else
520 		fmt = barrier ? " |  %s" : "  %s  ";
521 
522 	if (logo != NULL) {
523 		if (line < LOGO_LINES)
524 			printf(fmt, logo[line]);
525 		else
526 			printf(fmt, logo_blank_line);
527 	}
528 }
529 
530 static void
531 menu_display(void)
532 {
533 	dvar_t dvar;
534 	int i;
535 	int logo_left = 0;		/* default to fred on right */
536 	int separated = 0;		/* default blue fred without line */
537 	char **logo = logo_indigo;
538 	char *console_val = getenv("console");
539 
540 	if (dvar_istrue(dvar_get("fred_is_red")))
541 		logo = logo_color;
542 
543 	if (dvar_istrue(dvar_get("loader_plain")))
544 		logo = logo_mono;
545 
546 	if (strcmp(console_val, "comconsole") == 0)
547 		logo = logo_mono;
548 
549 	if (dvar_istrue(dvar_get("fred_disable")))
550 		logo = NULL;
551 
552 	if (dvar_istrue(dvar_get("fred_on_left")))
553 		logo_left = 1;
554 
555 	if (dvar_istrue(dvar_get("fred_separated")))
556 		separated = 1;
557 
558 	dvar = dvar_first();
559 	i = 0;
560 
561 	if (logo != NULL) {
562 		if (logo_left)
563 			printf(separated ? "%35s|%43s\n" : "%35s %43s\n",
564 				" ", " ");
565 		else
566 			printf(separated ? "%43s|%35s\n" : "%43s %35s\n",
567 				" ", " ");
568 	}
569 
570 	while (dvar || i < LOGO_LINES) {
571 		if (logo_left)
572 			logo_display(logo, i, FRED_LEFT, separated);
573 
574 		while (dvar) {
575 			if (strncmp(dvar->name, "menu_", 5) == 0) {
576 				printf(" %c. %-38.38s",
577 				    dvar->name[5], dvar->data[0]);
578 				dvar = dvar_next(dvar);
579 				break;
580 			}
581 			dvar = dvar_next(dvar);
582 		}
583 		/*
584 		 * Pad when the number of menu entries is less than
585 		 * LOGO_LINES.
586 		 */
587 		if (dvar == NULL)
588 			printf("    %38.38s", " ");
589 
590 		if (!logo_left)
591 			logo_display(logo, i, FRED_RIGHT, separated);
592 		printf("\n");
593 		i++;
594 	}
595 }
596 
597 static int
598 menu_execute(int c)
599 {
600 	dvar_t dvar;
601 	dvar_t dvar_exec = NULL;
602 	dvar_t *dvar_execp = &dvar_exec;
603 	char namebuf[32];
604 	int res;
605 
606 	snprintf(namebuf, sizeof(namebuf), "item_%c_0", c);
607 
608 	/*
609 	 * Does this menu option exist?
610 	 */
611 	if (dvar_get(namebuf) == NULL)
612 		return(-1);
613 
614 	snprintf(namebuf, sizeof(namebuf), "item_%c", c);
615 	res = CMD_OK;
616 	printf("\n");
617 
618 	/*
619 	 * Copy the items to execute (the act of execution may modify our
620 	 * local variables so we need to copy).
621 	 */
622 	for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
623 		if (strncmp(dvar->name, namebuf, 6) == 0) {
624 			*dvar_execp = dvar_copy(dvar);
625 			dvar_execp = &(*dvar_execp)->next;
626 		}
627 	}
628 
629 	/*
630 	 * Execute items
631 	 */
632 	for (dvar = dvar_exec; dvar; dvar = dvar->next) {
633 		res = perform(dvar->count, dvar->data);
634 		if (res != CMD_OK) {
635 			printf("%s: %s\n",
636 				dvar->data[0], command_errmsg);
637 			setenv("autoboot_delay", "NO", 1);
638 			break;
639 		}
640 	}
641 
642 	/*
643 	 * Free items
644 	 */
645 	while (dvar_exec)
646 		dvar_free(&dvar_exec);
647 
648 	return(res);
649 }
650