xref: /minix/usr.sbin/installboot/installboot.c (revision 0a6a1f1d)
1 /*	$NetBSD: installboot.c,v 1.39 2015/07/25 10:37:22 mlelstv Exp $	*/
2 
3 /*-
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Luke Mewburn of Wasabi Systems.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #endif
35 
36 #include <sys/cdefs.h>
37 #if !defined(__lint)
38 __RCSID("$NetBSD: installboot.c,v 1.39 2015/07/25 10:37:22 mlelstv Exp $");
39 #endif	/* !__lint */
40 
41 #include <sys/param.h>
42 #include <sys/ioctl.h>
43 #include <sys/utsname.h>
44 
45 #include <assert.h>
46 #include <err.h>
47 #include <fcntl.h>
48 #include <limits.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <stddef.h>
52 #include <string.h>
53 #include <unistd.h>
54 #if !HAVE_NBTOOL_CONFIG_H
55 #include <util.h>
56 #endif
57 
58 #include "installboot.h"
59 
60 static	void	getmachine(ib_params *, const char *, const char *);
61 static	void	getfstype(ib_params *, const char *, const char *);
62 static	void	parseoptions(ib_params *, const char *);
63 __dead static	void	usage(void);
64 static	void	options_usage(void);
65 static	void	machine_usage(void);
66 static	void	fstype_usage(void);
67 
68 static	ib_params	installboot_params;
69 
70 #define OFFSET(field)    offsetof(ib_params, field)
71 const struct option {
72 	const char	*name;		/* Name of option */
73 	ib_flags	flag;		/* Corresponding IB_xxx flag */
74 	enum {				/* Type of option value... */
75 		OPT_BOOL,		/* no value */
76 		OPT_INT,		/* numeric value */
77 		OPT_WORD,		/* space/tab/, terminated */
78 		OPT_STRING		/* null terminated */
79 	}		type;
80 	int		offset;		/* of field in ib_params */
81 } options[] = {
82 	{ "alphasum",	IB_ALPHASUM,	OPT_BOOL,	0 },
83 	{ "append",	IB_APPEND,	OPT_BOOL,	0 },
84 	{ "command",	IB_COMMAND,	OPT_STRING,	OFFSET(command) },
85 	{ "console",	IB_CONSOLE,	OPT_WORD,	OFFSET(console) },
86 	{ "ioaddr",	IB_CONSADDR,	OPT_INT,	OFFSET(consaddr) },
87 	{ "keymap",	IB_KEYMAP,	OPT_WORD,	OFFSET(keymap) },
88 	{ "password",	IB_PASSWORD,	OPT_WORD,	OFFSET(password) },
89 	{ "resetvideo",	IB_RESETVIDEO,	OPT_BOOL,	0 },
90 	{ "speed",	IB_CONSPEED,	OPT_INT,	OFFSET(conspeed) },
91 	{ "sunsum",	IB_SUNSUM,	OPT_BOOL,	0 },
92 	{ "timeout",	IB_TIMEOUT,	OPT_INT,	OFFSET(timeout) },
93 	{ "modules",	IB_MODULES,	OPT_BOOL,	0 },
94 	{ "bootconf",	IB_BOOTCONF,	OPT_BOOL,	0 },
95 	{ .name = NULL },
96 };
97 #undef OFFSET
98 #define OPTION(params, type, opt) (*(type *)((char *)(params) + (opt)->offset))
99 
100 #define DFL_SECSIZE	512	/* Don't use DEV_BSIZE. It's host's value. */
101 
102 int
main(int argc,char * argv[])103 main(int argc, char *argv[])
104 {
105 	struct utsname	utsname;
106 	ib_params	*params;
107 	unsigned long	lval;
108 	int		ch, rv, mode;
109 	char 		*p;
110 	const char	*op;
111 	ib_flags	unsupported_flags;
112 #if !HAVE_NBTOOL_CONFIG_H && !defined(__minix)
113 	char		specname[MAXPATHLEN];
114 	char		rawname[MAXPATHLEN];
115 	const char	*special, *raw;
116 #endif
117 
118 #if defined(__minix)
119 	/* XXX Temp stuff for MINIX until fdisk is ported */
120 	if ((4 <= argc && argc <= 6) && isoption(argv[1], "-master")) {
121 		install_master(argv[2], argv[3], argv + 4);
122 		exit(0);
123 	}
124 #endif /* defined(__minix) */
125 
126 	setprogname(argv[0]);
127 	params = &installboot_params;
128 	memset(params, 0, sizeof(*params));
129 	params->fsfd = -1;
130 	params->s1fd = -1;
131 	if ((p = getenv("MACHINE")) != NULL)
132 		getmachine(params, p, "$MACHINE");
133 
134 	while ((ch = getopt(argc, argv, "b:B:cefm:no:t:v")) != -1) {
135 		switch (ch) {
136 
137 		case 'b':
138 		case 'B':
139 			if (*optarg == '\0')
140 				goto badblock;
141 			lval = strtoul(optarg, &p, 0);
142 			if (lval > UINT32_MAX || *p != '\0') {
143  badblock:
144 				errx(1, "Invalid block number `%s'", optarg);
145 			}
146 			if (ch == 'b') {
147 				params->s1start = (uint32_t)lval;
148 				params->flags |= IB_STAGE1START;
149 			} else {
150 				params->s2start = (uint32_t)lval;
151 				params->flags |= IB_STAGE2START;
152 			}
153 			break;
154 
155 		case 'c':
156 			params->flags |= IB_CLEAR;
157 			break;
158 
159 		case 'e':
160 			params->flags |= IB_EDIT;
161 			break;
162 
163 		case 'f':
164 			params->flags |= IB_FORCE;
165 			break;
166 
167 		case 'm':
168 			getmachine(params, optarg, "-m");
169 			break;
170 
171 		case 'n':
172 			params->flags |= IB_NOWRITE;
173 			break;
174 
175 		case 'o':
176 			parseoptions(params, optarg);
177 			break;
178 
179 		case 't':
180 			getfstype(params, optarg, "-t");
181 			break;
182 
183 		case 'v':
184 			params->flags |= IB_VERBOSE;
185 			break;
186 
187 		case '?':
188 		default:
189 			usage();
190 			/* NOTREACHED */
191 
192 		}
193 	}
194 	argc -= optind;
195 	argv += optind;
196 
197 	if (params->flags & IB_CLEAR && params->flags & IB_EDIT)
198 		usage();
199 	if (argc < 1 || argc + 2 * !!(params->flags & (IB_CLEAR | IB_EDIT)) > 3)
200 		usage();
201 
202 	/* set missing defaults */
203 	if (params->machine == NULL) {
204 		if (uname(&utsname) == -1)
205 			err(1, "Determine uname");
206 		getmachine(params, utsname.machine, "uname()");
207 	}
208 
209 	/* Check that options are supported by this system */
210 	unsupported_flags = params->flags & ~params->machine->valid_flags;
211 	unsupported_flags &= ~(IB_VERBOSE | IB_NOWRITE | IB_CLEAR | IB_EDIT
212 				| IB_FORCE);
213 	if (unsupported_flags != 0) {
214 		int ndx;
215 		for (ndx = 0; options[ndx].name != NULL; ndx++) {
216 			if (unsupported_flags & options[ndx].flag) {
217 				unsupported_flags &= ~options[ndx].flag;
218 				warnx("`-o %s' is not supported for %s",
219 				    options[ndx].name, params->machine->name);
220 			}
221 		}
222 		if (unsupported_flags & IB_STAGE1START)
223 			warnx("`-b bno' is not supported for %s",
224 			    params->machine->name);
225 		if (unsupported_flags & IB_STAGE2START)
226 			warnx("`-B bno' is not supported for %s",
227 			    params->machine->name);
228 		unsupported_flags &= ~(IB_STAGE1START | IB_STAGE2START);
229 		if (unsupported_flags != 0)
230 			warnx("Unknown unsupported flag %#x (coding error!)",
231 			    unsupported_flags);
232 		exit(1);
233 	}
234 	/* and some illegal combinations */
235 	if (params->flags & IB_STAGE1START && params->flags & IB_APPEND) {
236 		warnx("Can't use `-b bno' with `-o append'");
237 		exit(1);
238 	}
239 	if (params->flags & IB_CLEAR &&
240 	    params->flags & (IB_STAGE1START | IB_STAGE2START | IB_APPEND)) {
241 		warnx("Can't use `-b bno', `-B bno' or `-o append' with `-c'");
242 		exit(1);
243 	}
244 
245 	if (argc >= 3) {
246 		params->stage2 = argv[2];
247 	}
248 
249 #if !HAVE_NBTOOL_CONFIG_H && !defined(__minix)
250 	special = getfsspecname(specname, sizeof(specname), argv[0]);
251 	if (special == NULL)
252 		err(1, "%s: %s", argv[0], specname);
253 	raw = getdiskrawname(rawname, sizeof(rawname), special);
254 	if (raw != NULL)
255 		special = raw;
256 	params->filesystem = special;
257 #else
258 	params->filesystem = argv[0];
259 #endif
260 
261 	if (params->flags & IB_NOWRITE) {
262 		op = "only";
263 		mode = O_RDONLY;
264 	} else {
265 		op = "write";
266 		mode = O_RDWR;
267 	}
268 #if defined(__minix)
269 	if (minixfs3_is_minix_partition(params)) {
270 		/* Old setups has just 1 sector for bootblock,
271 		 * but bootxx_minixfs is ~8Kb, so we require new setups
272 		 * to have 32 sectors before the first subpartition.
273 		 * This prevents from overwriting FS on old setups.
274 		 */
275 		if (!minixfs3_has_bootblock_space(params)) {
276 			err(1, "No space for bootxx, you should have 32 sectors"
277 				" before the first subpartition on %s",
278 				params->filesystem);
279 		}
280 	}
281 #endif /* defined(__minix) */
282 	/* XXX should be specified via option */
283 	params->sectorsize = DFL_SECSIZE;
284 	if ((params->fsfd = open(params->filesystem, mode, 0600)) == -1)
285 		err(1, "Opening file system `%s' read-%s",
286 		    params->filesystem, op);
287 	if (fstat(params->fsfd, &params->fsstat) == -1)
288 		err(1, "Examining file system `%s'", params->filesystem);
289 	if (params->fstype != NULL) {
290 		if (! params->fstype->match(params))
291 			errx(1, "File system `%s' is not of type %s",
292 			    params->filesystem, params->fstype->name);
293 	} else {
294 		if (params->stage2 != NULL) {
295 			params->fstype = &fstypes[0];
296 			while (params->fstype->name != NULL &&
297 				    !params->fstype->match(params))
298 				params->fstype++;
299 			if (params->fstype->name == NULL)
300 				errx(1, "File system `%s' is of an unknown type",
301 				    params->filesystem);
302 		}
303 	}
304 
305 	if (argc >= 2) {
306 		if ((params->s1fd = open(argv[1], O_RDONLY, 0600)) == -1)
307 			err(1, "Opening primary bootstrap `%s'", argv[1]);
308 		if (fstat(params->s1fd, &params->s1stat) == -1)
309 			err(1, "Examining primary bootstrap `%s'", argv[1]);
310 		if (!S_ISREG(params->s1stat.st_mode))
311 			errx(1, "`%s' must be a regular file", argv[1]);
312 		params->stage1 = argv[1];
313 	}
314 	assert(params->machine != NULL);
315 
316 	if (params->flags & IB_VERBOSE) {
317 		printf("File system:         %s\n", params->filesystem);
318 		if (params->fstype)
319 			printf("File system type:    %s (blocksize %u, "
320 				"needswap %d)\n",
321 			    params->fstype->name, params->fstype->blocksize,
322 			    params->fstype->needswap);
323 		if (!(params->flags & IB_EDIT))
324 			printf("Primary bootstrap:   %s\n",
325 			    (params->flags & IB_CLEAR) ? "(to be cleared)"
326 			    : params->stage1 ? params->stage1 : "(none)" );
327 		if (params->stage2 != NULL)
328 			printf("Secondary bootstrap: %s\n", params->stage2);
329 	}
330 
331 	if (params->flags & IB_EDIT) {
332 		op = "Edit";
333 		rv = params->machine->editboot(params);
334 	} else if (params->flags & IB_CLEAR) {
335 		op = "Clear";
336 		rv = params->machine->clearboot(params);
337 	} else {
338 		if (argc < 2)
339 			errx(EXIT_FAILURE, "Please specify the primary "
340 			    "bootstrap file");
341 		op = "Set";
342 		rv = params->machine->setboot(params);
343 	}
344 	if (rv == 0)
345 		errx(1, "%s bootstrap operation failed", op);
346 
347 	if (S_ISREG(params->fsstat.st_mode)) {
348 		if (fsync(params->fsfd) == -1)
349 			err(1, "Synchronising file system `%s'",
350 			    params->filesystem);
351 	} else {
352 		/* Sync filesystems (to clean in-memory superblock?) */
353 		sync();
354 	}
355 	if (close(params->fsfd) == -1)
356 		err(1, "Closing file system `%s'", params->filesystem);
357 	if (argc == 2)
358 		if (close(params->s1fd) == -1)
359 			err(1, "Closing primary bootstrap `%s'",
360 			    params->stage1);
361 
362 	exit(0);
363 	/* NOTREACHED */
364 }
365 
366 static void
parseoptions(ib_params * params,const char * option)367 parseoptions(ib_params *params, const char *option)
368 {
369 	char *cp;
370 	const struct option *opt;
371 	int len;
372 	unsigned long val;
373 
374 	assert(params != NULL);
375 	assert(option != NULL);
376 
377 	for (;; option += len) {
378 		option += strspn(option, ", \t");
379 		if (*option == 0)
380 			return;
381 		len = strcspn(option, "=,");
382 		for (opt = options; opt->name != NULL; opt++) {
383 			if (memcmp(option, opt->name, len) == 0
384 			    && opt->name[len] == 0)
385 				break;
386 		}
387 		if (opt->name == NULL) {
388 			len = strcspn(option, ",");
389 			warnx("Unknown option `-o %.*s'", len, option);
390 			break;
391 		}
392 		params->flags |= opt->flag;
393 		if (opt->type == OPT_BOOL) {
394 			if (option[len] != '=')
395 				continue;
396 			warnx("Option `%s' must not have a value", opt->name);
397 			break;
398 		}
399 		if (option[len] != '=') {
400 			warnx("Option `%s' must have a value", opt->name);
401 			break;
402 		}
403 		option += len + 1;
404 		len = strcspn(option, ",");
405 		switch (opt->type) {
406 		case OPT_STRING:
407 			len = strlen(option);
408 			/* FALLTHROUGH */
409 		case OPT_WORD:
410 			cp = strdup(option);
411 			if (cp == NULL)
412 				err(1, "strdup");
413 			cp[len] = 0;
414 			OPTION(params, char *, opt) = cp;
415 			continue;
416 		case OPT_INT:
417 			val = strtoul(option, &cp, 0);
418 			if (cp > option + len || (*cp != 0 && *cp != ','))
419 				break;
420 			if (val > INT_MAX)
421 				break;
422 			OPTION(params, int, opt) = (int)val;
423 			continue;
424 		default:
425 			errx(1, "Internal error: option `%s' has invalid type %d",
426 				opt->name, opt->type);
427 		}
428 		warnx("Invalid option value `%s=%.*s'", opt->name, len, option);
429 		break;
430 	}
431 	options_usage();
432 	exit(1);
433 }
434 
435 static void
options_usage(void)436 options_usage(void)
437 {
438 	int ndx;
439 	const char *pfx;
440 
441 	warnx("Valid options are:");
442 	pfx = "\t";
443 	for (ndx = 0; options[ndx].name != 0; ndx++) {
444 		fprintf(stderr, "%s%s", pfx, options[ndx].name);
445 		switch (options[ndx].type) {
446 		case OPT_INT:
447 			fprintf(stderr, "=number");
448 			break;
449 		case OPT_WORD:
450 			fprintf(stderr, "=word");
451 			break;
452 		case OPT_STRING:
453 			fprintf(stderr, "=string");
454 			break;
455 		default:
456 			break;
457 		}
458 		if ((ndx % 5) == 4)
459 			pfx = ",\n\t";
460 		else
461 			pfx = ", ";
462 	}
463 	fprintf(stderr, "\n");
464 }
465 
466 int
no_setboot(ib_params * params)467 no_setboot(ib_params *params)
468 {
469 
470 	assert(params != NULL);
471 
472 	warnx("%s: bootstrap installation is not supported",
473 	    params->machine->name);
474 	return (0);
475 }
476 
477 int
no_clearboot(ib_params * params)478 no_clearboot(ib_params *params)
479 {
480 
481 	assert(params != NULL);
482 
483 	warnx("%s: bootstrap removal is not supported",
484 	    params->machine->name);
485 	return (0);
486 }
487 
488 int
no_editboot(ib_params * params)489 no_editboot(ib_params *params)
490 {
491 
492 	assert(params != NULL);
493 
494 	warnx("%s: bootstrap editing is not supported",
495 	    params->machine->name);
496 	return (0);
497 }
498 
499 
500 static void
getmachine(ib_params * param,const char * mach,const char * provider)501 getmachine(ib_params *param, const char *mach, const char *provider)
502 {
503 	int	i;
504 
505 	assert(param != NULL);
506 	assert(mach != NULL);
507 	assert(provider != NULL);
508 
509 	for (i = 0; machines[i] != NULL; i++) {
510 		if (machines[i]->name == NULL)
511 			continue;
512 		if (strcmp(machines[i]->name, mach) == 0) {
513 			param->machine = machines[i];
514 			return;
515 		}
516 	}
517 	warnx("Invalid machine `%s' from %s", mach, provider);
518 	machine_usage();
519 	exit(1);
520 }
521 
522 static void
machine_usage(void)523 machine_usage(void)
524 {
525 	const char *prefix;
526 	int	i;
527 	int col, len;
528 	const char *name;
529 	int	wincol=80;
530 #ifdef TIOCGWINSZ
531 	struct winsize win;
532 
533 	if (ioctl(fileno(stderr), TIOCGWINSZ, &win) == 0 && win.ws_col > 0)
534 		wincol = win.ws_col;
535 #endif
536 
537 	warnx("Supported machines are:");
538 	prefix="\t";
539 	col = 8 + 3;
540 	for (i = 0; machines[i] != NULL; i++) {
541 		name = machines[i]->name;
542 		if (name == NULL)
543 			continue;
544 		len = strlen(name);
545 		if (col + len > wincol) {
546 			prefix=",\n\t";
547 			col = -2 + 8 + 3;
548 		}
549 		col += fprintf(stderr, "%s%s", prefix, name);
550 		prefix=", ";
551 	}
552 	fputs("\n", stderr);
553 }
554 
555 static void
getfstype(ib_params * param,const char * fstype,const char * provider)556 getfstype(ib_params *param, const char *fstype, const char *provider)
557 {
558 	int i;
559 
560 	assert(param != NULL);
561 	assert(fstype != NULL);
562 	assert(provider != NULL);
563 
564 	for (i = 0; fstypes[i].name != NULL; i++) {
565 		if (strcmp(fstypes[i].name, fstype) == 0) {
566 			param->fstype = &fstypes[i];
567 			return;
568 		}
569 	}
570 	warnx("Invalid file system type `%s' from %s", fstype, provider);
571 	fstype_usage();
572 	exit(1);
573 }
574 
575 static void
fstype_usage(void)576 fstype_usage(void)
577 {
578 #ifndef NO_STAGE2
579 	const char *prefix;
580 	int	i;
581 
582 	warnx("Supported file system types are:");
583 #define FSTYPES_PER_LINE	9
584 	prefix="\t";
585 	for (i = 0; fstypes[i].name != NULL; i++) {
586 		if (i && (i % FSTYPES_PER_LINE) == 0)
587 			prefix=",\n\t";
588 		fprintf(stderr, "%s%s", prefix, fstypes[i].name);
589 		prefix=", ";
590 	}
591 	fputs("\n", stderr);
592 #endif
593 }
594 
595 static void
usage(void)596 usage(void)
597 {
598 	const char	*prog;
599 
600 	prog = getprogname();
601 	fprintf(stderr,
602 "usage: %s [-fnv] [-B s2bno] [-b s1bno] [-m machine] [-o options]\n"
603 "\t\t   [-t fstype] filesystem primary [secondary]\n"
604 "usage: %s -c [-fnv] [-m machine] [-o options] [-t fstype] filesystem\n"
605 "usage: %s -e [-fnv] [-m machine] [-o options] bootstrap\n",
606 	    prog, prog, prog);
607 	machine_usage();
608 	fstype_usage();
609 	options_usage();
610 	exit(1);
611 }
612