1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
5 * All Rights Reserved.
6 * Copyright (c) 1998 Robert Nordier
7 * All Rights Reserved.
8 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
9 * All rights reserved.
10 * Copyright (c) 2014 Roger Pau Monn�� <roger.pau@citrix.com>
11 * All Rights Reserved.
12 * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
13 * Copyright (c) 2018 Netflix, Inc.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 *    notice, this list of conditions and the following disclaimer
20 *    in this position and unchanged.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 *    notice, this list of conditions and the following disclaimer in the
23 *    documentation and/or other materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#include <sys/cdefs.h>
39/* Note: This is compiled in both the kernel and boot loader contexts */
40
41#include <sys/param.h>
42#ifdef _KERNEL
43#include <sys/systm.h>
44#else
45#include <stand.h>
46#endif
47#include <sys/reboot.h>
48#include <sys/boot.h>
49#include <sys/tslog.h>
50
51#ifdef _KERNEL
52#define SETENV(k, v)	kern_setenv(k, v)
53#define GETENV(k)	kern_getenv(k)
54#define FREE(v)		freeenv(v)
55#else	/* Boot loader */
56#define SETENV(k, v)	setenv(k, v, 1)
57#define GETENV(k)	getenv(k)
58#define	FREE(v)
59#endif
60
61static struct
62{
63	const char	*ev;
64	int		mask;
65} howto_names[] = {
66	{ "boot_askname",	RB_ASKNAME},
67	{ "boot_cdrom",		RB_CDROM},
68	{ "boot_ddb",		RB_KDB},
69	{ "boot_dfltroot",	RB_DFLTROOT},
70	{ "boot_gdb",		RB_GDB},
71	{ "boot_multicons",	RB_MULTIPLE},
72	{ "boot_mute",		RB_MUTE},
73	{ "boot_mutemsgs",	RB_MUTEMSGS},
74	{ "boot_pause",		RB_PAUSE},
75	{ "boot_serial",	RB_SERIAL},
76	{ "boot_single",	RB_SINGLE},
77	{ "boot_verbose",	RB_VERBOSE},
78	{ NULL,	0}
79};
80
81/*
82 * In the boot environment, we often parse a command line and have to throw away
83 * its contents. As we do so, we set environment variables that correspond to
84 * the flags we encounter. Later, to get a howto mask, we grovel through these
85 * to reconstruct it. This also allows users in their loader.conf to set them
86 * and have the kernel see them.
87 */
88
89/**
90 * @brief convert the env vars in howto_names into a howto mask
91 */
92int
93boot_env_to_howto(void)
94{
95	int i, howto;
96	char *val;
97
98	TSENTER();
99	for (howto = 0, i = 0; howto_names[i].ev != NULL; i++) {
100		val = GETENV(howto_names[i].ev);
101		if (val != NULL && strcasecmp(val, "no") != 0)
102			howto |= howto_names[i].mask;
103		FREE(val);
104	}
105	TSEXIT();
106	return (howto);
107}
108
109/**
110 * @brief Set env vars from howto_names based on howto passed in
111 */
112void
113boot_howto_to_env(int howto)
114{
115	int i;
116
117	for (i = 0; howto_names[i].ev != NULL; i++)
118		if (howto & howto_names[i].mask)
119			SETENV(howto_names[i].ev, "YES");
120}
121
122/**
123 * @brief Helper routine to parse a single arg and return its mask
124 *
125 * Parse all the - options to create a mask (or a serial speed in the
126 * case of -S). If the arg doesn't start with '-' assume it's an env
127 * variable and set that instead.
128 */
129int
130boot_parse_arg(const char *v)
131{
132	char *n;
133	int howto;
134
135#if 0
136/* Need to see if this is better or worse than the meat of the #else */
137static const char howto_switches[] = "aCdrgDmMphsv";
138static int howto_masks[] = {
139	RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE,
140	RB_MUTE, RB_MUTEMSGS, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE
141};
142
143	opts = strchr(kargs, '-');
144	while (opts != NULL) {
145		while (*(++opts) != '\0') {
146			sw = strchr(howto_switches, *opts);
147			if (sw == NULL)
148				break;
149			howto |= howto_masks[sw - howto_switches];
150		}
151		opts = strchr(opts, '-');
152	}
153#else
154	howto = 0;
155	if (*v == '-') {
156		while (*v != '\0') {
157			v++;
158			switch (*v) {
159			case 'a': howto |= RB_ASKNAME; break;
160			case 'C': howto |= RB_CDROM; break;
161			case 'd': howto |= RB_KDB; break;
162			case 'D': howto |= RB_MULTIPLE; break;
163			case 'm': howto |= RB_MUTE; break;
164			case 'M': howto |= RB_MUTEMSGS; break;
165			case 'g': howto |= RB_GDB; break;
166			case 'h': howto |= RB_SERIAL; break;
167			case 'p': howto |= RB_PAUSE; break;
168			case 'P': howto |= RB_PROBE; break;
169			case 'r': howto |= RB_DFLTROOT; break;
170			case 's': howto |= RB_SINGLE; break;
171			case 'S': SETENV("comconsole_speed", v + 1); v += strlen(v); break;
172			case 'v': howto |= RB_VERBOSE; break;
173			}
174		}
175	} else {
176		char buf[128];
177		char *vv = buf;
178
179		strlcpy(buf, v, sizeof(buf));
180		n = strsep(&vv, "=");
181		if (vv == NULL)
182			SETENV(n, "1");
183		else
184			SETENV(n, vv);
185	}
186#endif
187	return (howto);
188}
189
190/**
191 * @brief breakup the command line into args, and pass to boot_parse_arg
192 */
193int
194boot_parse_cmdline_delim(char *cmdline, const char *delim)
195{
196	char *v;
197	int howto;
198
199	TSENTER();
200	howto = 0;
201	while ((v = strsep(&cmdline, delim)) != NULL) {
202		if (*v == '\0')
203			continue;
204		howto |= boot_parse_arg(v);
205	}
206	TSEXIT();
207	return (howto);
208}
209
210/**
211 * @brief Simplified interface for common 'space or tab separated' args
212 */
213int
214boot_parse_cmdline(char *cmdline)
215{
216
217	return (boot_parse_cmdline_delim(cmdline, " \t\n"));
218}
219
220/**
221 * @brief Pass a vector of strings to boot_parse_arg
222 */
223int
224boot_parse_args(int argc, char *argv[])
225{
226        int i, howto;
227
228	howto = 0;
229        for (i = 1; i < argc; i++)
230                howto |= boot_parse_arg(argv[i]);
231	return (howto);
232}
233