Home | History | Annotate | Line # | Download | only in libsa
      1  1.10  pgoyette /*	$NetBSD: bootcfg.c,v 1.10 2025/05/06 18:16:12 pgoyette Exp $	*/
      2   1.1       rtr 
      3   1.1       rtr /*-
      4   1.1       rtr  * Copyright (c) 2008 The NetBSD Foundation, Inc.
      5   1.1       rtr  * All rights reserved.
      6   1.1       rtr  *
      7   1.1       rtr  * Redistribution and use in source and binary forms, with or without
      8   1.1       rtr  * modification, are permitted provided that the following conditions
      9   1.1       rtr  * are met:
     10   1.1       rtr  * 1. Redistributions of source code must retain the above copyright
     11   1.1       rtr  *    notice, this list of conditions and the following disclaimer.
     12   1.1       rtr  * 2. Redistributions in binary form must reproduce the above copyright
     13   1.1       rtr  *    notice, this list of conditions and the following disclaimer in the
     14   1.1       rtr  *    documentation and/or other materials provided with the distribution.
     15   1.1       rtr  *
     16   1.1       rtr  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17   1.1       rtr  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18   1.1       rtr  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19   1.1       rtr  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20   1.1       rtr  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21   1.1       rtr  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22   1.1       rtr  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23   1.1       rtr  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24   1.1       rtr  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25   1.1       rtr  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26   1.1       rtr  * POSSIBILITY OF SUCH DAMAGE.
     27   1.1       rtr  */
     28   1.1       rtr 
     29   1.1       rtr #include <sys/types.h>
     30   1.1       rtr #include <sys/reboot.h>
     31   1.1       rtr 
     32   1.1       rtr #include <lib/libsa/stand.h>
     33   1.1       rtr #include <lib/libsa/bootcfg.h>
     34   1.1       rtr #include <lib/libkern/libkern.h>
     35   1.1       rtr 
     36   1.1       rtr #define MENUFORMAT_AUTO   0
     37   1.1       rtr #define MENUFORMAT_NUMBER 1
     38   1.1       rtr #define MENUFORMAT_LETTER 2
     39   1.1       rtr 
     40   1.1       rtr #define DEFAULT_FORMAT  MENUFORMAT_AUTO
     41   1.8  jmcneill #ifndef DEFAULT_TIMEOUT
     42   1.1       rtr #define DEFAULT_TIMEOUT 10
     43   1.8  jmcneill #endif
     44   1.1       rtr 
     45   1.1       rtr struct bootcfg_def bootcfg_info;
     46   1.1       rtr 
     47   1.1       rtr void
     48   1.1       rtr bootcfg_do_noop(const char *cmd, char *arg)
     49   1.1       rtr {
     50   1.1       rtr 	/* noop, do nothing */
     51   1.1       rtr }
     52   1.1       rtr 
     53   1.1       rtr /*
     54   1.1       rtr  * This function parses a boot.cfg file in the root of the filesystem
     55   1.1       rtr  * (if present) and populates the global boot configuration.
     56   1.1       rtr  *
     57   1.1       rtr  * The file consists of a number of lines each terminated by \n
     58   1.1       rtr  * The lines are in the format keyword=value. There should not be spaces
     59   1.1       rtr  * around the = sign.
     60   1.1       rtr  *
     61   1.1       rtr  * perform_bootcfg(conf, command, maxsz)
     62   1.1       rtr  *
     63   1.1       rtr  * conf		Path to boot.cfg to be passed verbatim to open()
     64   1.1       rtr  *
     65   1.1       rtr  * command	Pointer to a function that will be called when
     66   1.1       rtr  * 		perform_bootcfg() encounters a key (command) it does not
     67   1.1       rtr  *		recognize.
     68   1.1       rtr  *		The command function is provided both the keyword and
     69   1.1       rtr  *		value parsed as arguments to the function.
     70   1.1       rtr  *
     71   1.1       rtr  * maxsz	Limit the size of the boot.cfg perform_bootcfg() will parse.
     72   1.1       rtr  * 		- If maxsz is < 0 boot.cfg will not be processed.
     73   1.1       rtr  * 		- If maxsz is = 0 no limit will be imposed but parsing may
     74   1.1       rtr  *		  fail due to platform or other constraints e.g. maximum
     75   1.1       rtr  *		  segment size.
     76   1.1       rtr  *		- If 0 < maxsz and boot.cfg exceeds maxsz it will not be
     77   1.1       rtr  *		  parsed, otherwise it will be parsed.
     78   1.1       rtr  *
     79   1.1       rtr  * The recognised keywords are:
     80   1.1       rtr  * banner: text displayed instead of the normal welcome text
     81   1.1       rtr  * menu: Descriptive text:command to use
     82   1.1       rtr  * timeout: Timeout in seconds (overrides that set by installboot)
     83   1.1       rtr  * default: the default menu option to use if Return is pressed
     84   1.1       rtr  * consdev: the console device to use
     85   1.6   mlelstv  * root: the root device to use
     86   1.1       rtr  * format: how menu choices are displayed: (a)utomatic, (n)umbers or (l)etters
     87   1.1       rtr  * clear: whether to clear the screen or not
     88   1.1       rtr  *
     89   1.1       rtr  * Example boot.cfg file:
     90   1.1       rtr  * banner=Welcome to NetBSD
     91   1.1       rtr  * banner=Please choose the boot type from the following menu
     92   1.1       rtr  * menu=Boot NetBSD:boot netbsd
     93   1.1       rtr  * menu=Boot into single user mode:boot netbsd -s
     94   1.1       rtr  * menu=:boot hd1a:netbsd -cs
     95   1.9    andvar  * menu=Goto boot command line:prompt
     96   1.1       rtr  * timeout=10
     97   1.1       rtr  * consdev=com0
     98   1.1       rtr  * default=1
     99   1.1       rtr */
    100   1.3    nonaka int
    101   1.1       rtr perform_bootcfg(const char *conf, bootcfg_command command, const off_t maxsz)
    102   1.1       rtr {
    103   1.1       rtr 	char *bc, *c;
    104   1.4  christos 	int cmenu, cbanner;
    105   1.5  jmcneill 	ssize_t len, off, resid;
    106   1.4  christos 	int fd, err;
    107   1.1       rtr 	struct stat st;
    108   1.1       rtr 	char *next, *key, *value, *v2;
    109   1.1       rtr 
    110   1.1       rtr 	/* clear bootcfg structure */
    111   1.1       rtr 	memset(&bootcfg_info, 0, sizeof(bootcfg_info));
    112   1.1       rtr 
    113   1.1       rtr 	/* set default timeout */
    114   1.1       rtr 	bootcfg_info.timeout = DEFAULT_TIMEOUT;
    115   1.1       rtr 
    116   1.1       rtr 	/* automatically switch between letter and numbers on menu */
    117   1.1       rtr 	bootcfg_info.menuformat = DEFAULT_FORMAT;
    118   1.1       rtr 
    119   1.1       rtr 	fd = open(conf, 0);
    120   1.1       rtr 	if (fd < 0)
    121   1.3    nonaka 		return ENOENT;
    122   1.1       rtr 
    123   1.1       rtr 	err = fstat(fd, &st);
    124   1.1       rtr 	if (err == -1) {
    125   1.5  jmcneill 		/* file descriptor may not be backed by a libsa file-system */
    126   1.5  jmcneill 		st.st_size = maxsz;
    127   1.1       rtr 	}
    128   1.1       rtr 
    129   1.1       rtr 	/* if a maximum size is being requested for the boot.cfg enforce it. */
    130   1.1       rtr 	if (0 < maxsz && st.st_size > maxsz) {
    131   1.1       rtr 		close(fd);
    132   1.3    nonaka 		return EFBIG;
    133   1.1       rtr 	}
    134   1.1       rtr 
    135   1.4  christos 	bc = alloc((size_t)st.st_size + 1);
    136   1.1       rtr 	if (bc == NULL) {
    137   1.1       rtr 		printf("Could not allocate memory for boot configuration\n");
    138   1.1       rtr 		close(fd);
    139   1.3    nonaka 		return ENOMEM;
    140   1.1       rtr 	}
    141   1.1       rtr 
    142   1.1       rtr 	/*
    143   1.1       rtr 	 * XXX original code, assumes error or eof return from read()
    144   1.1       rtr 	 *     results in the entire boot.cfg being buffered.
    145   1.1       rtr 	 *     - should bail out on read() failing.
    146   1.1       rtr 	 *     - assumption is made that the file size doesn't change between
    147   1.1       rtr 	 *       fstat() and read()ing.  probably safe in this context
    148   1.1       rtr 	 *       arguably should check that reading the file won't overflow
    149   1.1       rtr 	 *       the storage anyway.
    150   1.1       rtr 	 */
    151   1.1       rtr 	off = 0;
    152   1.5  jmcneill 	resid = st.st_size;
    153   1.1       rtr 	do {
    154   1.5  jmcneill 		len = read(fd, bc + off, uimin(1024, resid));
    155   1.1       rtr 		if (len <= 0)
    156   1.1       rtr 			break;
    157   1.1       rtr 		off += len;
    158   1.5  jmcneill 		resid -= len;
    159   1.5  jmcneill 	} while (len > 0 && resid > 0);
    160   1.1       rtr 	bc[off] = '\0';
    161   1.1       rtr 
    162   1.1       rtr 	close(fd);
    163   1.1       rtr 
    164   1.1       rtr 	/* bc is now assumed to contain the whole boot.cfg file (see above) */
    165   1.1       rtr 
    166   1.1       rtr 	cmenu = 0;
    167   1.1       rtr 	cbanner = 0;
    168   1.1       rtr 	for (c = bc; *c; c = next) {
    169   1.1       rtr 		key = c;
    170   1.1       rtr 		/* find end of line */
    171   1.1       rtr 		for (; *c && *c != '\n'; c++)
    172   1.1       rtr 			/* zero terminate line on start of comment */
    173   1.1       rtr 			if (*c == '#')
    174   1.1       rtr 				*c = 0;
    175   1.1       rtr 		/* zero terminate line */
    176   1.1       rtr 		if (*(next = c))
    177   1.1       rtr 			*next++ = 0;
    178   1.1       rtr 		/* Look for = separator between key and value */
    179   1.1       rtr 		for (c = key; *c && *c != '='; c++)
    180   1.1       rtr 			continue;
    181   1.1       rtr 		/* Ignore lines with no key=value pair */
    182   1.1       rtr 		if (*c == '\0')
    183   1.1       rtr 			continue;
    184   1.1       rtr 
    185   1.1       rtr 		/* zero terminate key which points to keyword */
    186   1.1       rtr 		*c++ = 0;
    187   1.1       rtr 		value = c;
    188   1.1       rtr 		/* Look for end of line (or file) and zero terminate value */
    189   1.1       rtr 		for (; *c && *c != '\n'; c++)
    190   1.1       rtr 			continue;
    191   1.1       rtr 		*c = 0;
    192   1.1       rtr 
    193   1.1       rtr 		if (!strncmp(key, "menu", 4)) {
    194   1.1       rtr 			/*
    195   1.1       rtr 			 * Parse "menu=<description>:<command>".  If the
    196   1.1       rtr 			 * description is empty ("menu=:<command>)",
    197   1.1       rtr 			 * then re-use the command as the description.
    198   1.1       rtr 			 * Note that the command may contain embedded
    199   1.1       rtr 			 * colons.
    200   1.1       rtr 			 */
    201   1.1       rtr 			if (cmenu >= BOOTCFG_MAXMENU)
    202   1.1       rtr 				continue;
    203   1.1       rtr 			bootcfg_info.desc[cmenu] = value;
    204   1.1       rtr 			for (v2 = value; *v2 && *v2 != ':'; v2++)
    205   1.1       rtr 				continue;
    206   1.1       rtr 			if (*v2) {
    207   1.1       rtr 				*v2++ = 0;
    208   1.1       rtr 				bootcfg_info.command[cmenu] = v2;
    209   1.1       rtr 				if (! *value)
    210   1.1       rtr 					bootcfg_info.desc[cmenu] = v2;
    211   1.1       rtr 				cmenu++;
    212   1.1       rtr 			} else {
    213   1.1       rtr 				/* No delimiter means invalid line */
    214   1.1       rtr 				bootcfg_info.desc[cmenu] = NULL;
    215   1.1       rtr 			}
    216   1.1       rtr 		} else if (!strncmp(key, "banner", 6)) {
    217   1.1       rtr 			if (cbanner < BOOTCFG_MAXBANNER)
    218   1.1       rtr 				bootcfg_info.banner[cbanner++] = value;
    219   1.1       rtr 		} else if (!strncmp(key, "timeout", 7)) {
    220   1.2     isaki 			if (!isdigit(*value))
    221   1.1       rtr 				bootcfg_info.timeout = -1;
    222   1.1       rtr 			else
    223   1.1       rtr 				bootcfg_info.timeout = atoi(value);
    224   1.1       rtr 		} else if (!strncmp(key, "default", 7)) {
    225   1.1       rtr 			bootcfg_info.def = atoi(value) - 1;
    226   1.1       rtr 		} else if (!strncmp(key, "consdev", 7)) {
    227   1.1       rtr 			bootcfg_info.consdev = value;
    228   1.6   mlelstv 		} else if (!strncmp(key, "root", 4)) {
    229   1.6   mlelstv 			bootcfg_info.root = value;
    230   1.1       rtr 		} else if (!strncmp(key, "format", 6)) {
    231   1.1       rtr 			printf("value:%c\n", *value);
    232   1.1       rtr 			switch (*value) {
    233   1.1       rtr 			case 'a':
    234   1.1       rtr 			case 'A':
    235   1.1       rtr 				bootcfg_info.menuformat = MENUFORMAT_AUTO;
    236   1.1       rtr 				break;
    237   1.1       rtr 
    238   1.1       rtr 			case 'n':
    239   1.1       rtr 			case 'N':
    240   1.1       rtr 			case 'd':
    241   1.1       rtr 			case 'D':
    242   1.1       rtr 				bootcfg_info.menuformat = MENUFORMAT_NUMBER;
    243   1.1       rtr 				break;
    244   1.1       rtr 
    245   1.1       rtr 			case 'l':
    246   1.1       rtr 			case 'L':
    247   1.1       rtr 				bootcfg_info.menuformat = MENUFORMAT_LETTER;
    248   1.1       rtr 				break;
    249   1.1       rtr 			}
    250   1.1       rtr 		} else if (!strncmp(key, "clear", 5)) {
    251   1.1       rtr 			bootcfg_info.clear = !!atoi(value);
    252   1.1       rtr 		} else {
    253   1.1       rtr 			command(key, value);
    254   1.1       rtr 		}
    255   1.1       rtr 	}
    256   1.1       rtr 
    257   1.1       rtr 	switch (bootcfg_info.menuformat) {
    258   1.1       rtr 	case MENUFORMAT_AUTO:
    259   1.1       rtr 		if (cmenu > 9 && bootcfg_info.timeout > 0)
    260   1.1       rtr 			bootcfg_info.menuformat = MENUFORMAT_LETTER;
    261   1.1       rtr 		else
    262   1.1       rtr 			bootcfg_info.menuformat = MENUFORMAT_NUMBER;
    263   1.1       rtr 		break;
    264   1.1       rtr 
    265   1.1       rtr 	case MENUFORMAT_NUMBER:
    266   1.1       rtr 		if (cmenu > 9 && bootcfg_info.timeout > 0)
    267   1.1       rtr 			cmenu = 9;
    268   1.1       rtr 		break;
    269   1.1       rtr 	}
    270   1.1       rtr 
    271   1.1       rtr 	bootcfg_info.nummenu = cmenu;
    272   1.1       rtr 	if (bootcfg_info.def < 0)
    273   1.1       rtr 		bootcfg_info.def = 0;
    274   1.1       rtr 	if (bootcfg_info.def >= cmenu)
    275   1.1       rtr 		bootcfg_info.def = cmenu - 1;
    276   1.3    nonaka 
    277   1.3    nonaka 	return 0;
    278   1.1       rtr }
    279   1.7       nia 
    280   1.7       nia void
    281   1.7       nia print_bootcfg_banner(const char *bootprog_name, const char *bootprog_rev)
    282   1.7       nia {
    283   1.7       nia 	int n = 0;
    284   1.7       nia 
    285   1.7       nia 	if (bootcfg_info.banner[0]) {
    286   1.7       nia 		for (; n < BOOTCFG_MAXBANNER && bootcfg_info.banner[n]; n++)
    287   1.7       nia 			printf("%s\n", bootcfg_info.banner[n]);
    288   1.7       nia 		return;
    289   1.7       nia 	}
    290   1.7       nia 
    291   1.7       nia 	/* If the user has not specified a banner, print a default one. */
    292   1.7       nia 
    293   1.7       nia 	printf("\n");
    294   1.7       nia 	printf("  \\\\-__,------,___.\n");
    295   1.7       nia 	printf("   \\\\        __,---`  %s\n", bootprog_name);
    296   1.7       nia 	printf("    \\\\       `---,_.  Revision %s\n", bootprog_rev);
    297   1.7       nia 	printf("     \\\\-,_____,.---`\n");
    298   1.7       nia 	printf("      \\\\\n");
    299   1.7       nia 	printf("       \\\\\n");
    300   1.7       nia 	printf("        \\\\\n\n");
    301   1.7       nia }
    302