installboot.c revision 1.15 1 /* $NetBSD: installboot.c,v 1.15 2004/03/13 22:51:50 dsl 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #if defined(__RCSID) && !defined(__lint)
41 __RCSID("$NetBSD: installboot.c,v 1.15 2004/03/13 22:51:50 dsl Exp $");
42 #endif /* !__lint */
43
44 #include <sys/utsname.h>
45
46 #include <assert.h>
47 #include <err.h>
48 #include <fcntl.h>
49 #include <limits.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <stddef.h>
53 #include <string.h>
54 #include <unistd.h>
55
56 #include "installboot.h"
57
58 int main(int, char *[]);
59 static void getmachine(ib_params *, const char *, const char *);
60 static void getfstype(ib_params *, const char *, const char *);
61 static void parseoptions(ib_params *, const char *);
62 static void usage(void);
63 static void options_usage(void);
64 static void machine_usage(void);
65 static void fstype_usage(void);
66
67 static ib_params installboot_params;
68
69 #define OFFSET(field) offsetof(ib_params, field)
70 const struct option {
71 const char *name; /* Name of option */
72 ib_flags flag; /* Corresponding IB_xxx flag */
73 enum { /* Type of option value... */
74 OPT_BOOL, /* no value */
75 OPT_INT, /* numeric value */
76 OPT_WORD, /* space/tab/, terminated */
77 OPT_STRING /* null terminated */
78 } type;
79 int offset; /* of field in ib_params */
80 } options[] = {
81 { "alphasum", IB_ALPHASUM, OPT_BOOL },
82 { "append", IB_APPEND, OPT_BOOL },
83 { "command", IB_COMMAND, OPT_STRING, OFFSET(command) },
84 { "console", IB_CONSOLE, OPT_WORD, OFFSET(console) },
85 { "keymap", IB_KEYMAP, OPT_WORD, OFFSET(keymap) },
86 { "password", IB_PASSWORD, OPT_WORD, OFFSET(password) },
87 { "resetvideo", IB_RESETVIDEO, OPT_BOOL },
88 { "speed", IB_CONSPEED, OPT_INT, OFFSET(conspeed) },
89 { "sunsum", IB_SUNSUM, OPT_BOOL },
90 { "timeout", IB_TIMEOUT, OPT_INT, OFFSET(timeout) },
91 { NULL },
92 };
93 #undef OFFSET
94 #define OPTION(params, type, opt) (*(type *)((char *)(params) + (opt)->offset))
95
96 int
97 main(int argc, char *argv[])
98 {
99 struct utsname utsname;
100 ib_params *params;
101 unsigned long lval;
102 int ch, rv, mode;
103 char *p;
104 const char *op;
105 ib_flags unsupported_flags;
106
107 setprogname(argv[0]);
108 params = &installboot_params;
109 memset(params, 0, sizeof(*params));
110 params->fsfd = -1;
111 params->s1fd = -1;
112 if ((p = getenv("MACHINE")) != NULL)
113 getmachine(params, p, "$MACHINE");
114
115 while ((ch = getopt(argc, argv, "b:B:cm:no:t:v")) != -1) {
116 switch (ch) {
117
118 case 'b':
119 case 'B':
120 if (*optarg == '\0')
121 goto badblock;
122 lval = strtoul(optarg, &p, 0);
123 if (lval > UINT32_MAX || *p != '\0') {
124 badblock:
125 errx(1, "Invalid block number `%s'", optarg);
126 }
127 if (ch == 'b') {
128 params->s1start = (uint32_t)lval;
129 params->flags |= IB_STAGE1START;
130 } else {
131 params->s2start = (uint32_t)lval;
132 params->flags |= IB_STAGE2START;
133 }
134 break;
135
136 case 'c':
137 params->flags |= IB_CLEAR;
138 break;
139
140 case 'm':
141 getmachine(params, optarg, "-m");
142 break;
143
144 case 'n':
145 params->flags |= IB_NOWRITE;
146 break;
147
148 case 'o':
149 parseoptions(params, optarg);
150 break;
151
152 case 't':
153 getfstype(params, optarg, "-t");
154 break;
155
156 case 'v':
157 params->flags |= IB_VERBOSE;
158 break;
159
160 case '?':
161 default:
162 usage();
163 /* NOTREACHED */
164
165 }
166 }
167 argc -= optind;
168 argv += optind;
169
170 if (((params->flags & IB_CLEAR) != 0 && argc != 1) ||
171 ((params->flags & IB_CLEAR) == 0 && (argc < 2 || argc > 3)))
172 usage();
173
174 /* set missing defaults */
175 if (params->machine == NULL) {
176 if (uname(&utsname) == -1)
177 err(1, "Determine uname");
178 getmachine(params, utsname.machine, "uname()");
179 }
180
181 /* Check that options are supported by this system */
182 unsupported_flags = params->flags & ~params->machine->valid_flags;
183 unsupported_flags &= ~(IB_VERBOSE | IB_NOWRITE |IB_CLEAR);
184 if (unsupported_flags != 0) {
185 int ndx;
186 for (ndx = 0; options[ndx].name != NULL; ndx++) {
187 if (unsupported_flags & options[ndx].flag)
188 warnx("`-o %s' is not supported for %s",
189 options[ndx].name, params->machine->name);
190 }
191 if (unsupported_flags & IB_STAGE1START)
192 warnx("`-b bno' is not supported for %s",
193 params->machine->name);
194 if (unsupported_flags & IB_STAGE2START)
195 warnx("`-B bno' is not supported for %s",
196 params->machine->name);
197 exit(1);
198 }
199 /* and some illegal combinations */
200 if (params->flags & IB_STAGE1START && params->flags & IB_APPEND) {
201 warnx("Can't use `-b bno' with `-o append'");
202 exit(1);
203 }
204 if (params->flags & IB_CLEAR &&
205 params->flags & (IB_STAGE1START | IB_STAGE2START | IB_APPEND)) {
206 warnx("Can't use `-b bno', `-B bno' or `-o append' with `-c'");
207 exit(1);
208 }
209
210 params->filesystem = argv[0];
211 if (params->flags & IB_NOWRITE) {
212 op = "only";
213 mode = O_RDONLY;
214 } else {
215 op = "write";
216 mode = O_RDWR;
217 }
218 if ((params->fsfd = open(params->filesystem, mode, 0600)) == -1)
219 err(1, "Opening file system `%s' read-%s",
220 params->filesystem, op);
221 if (fstat(params->fsfd, ¶ms->fsstat) == -1)
222 err(1, "Examining file system `%s'", params->filesystem);
223 if (params->fstype != NULL) {
224 if (! params->fstype->match(params))
225 err(1, "File system `%s' is not of type %s",
226 params->filesystem, params->fstype->name);
227 } else {
228 params->fstype = &fstypes[0];
229 while (params->fstype->name != NULL &&
230 ! params->fstype->match(params))
231 params->fstype++;
232 if (params->fstype->name == NULL)
233 errx(1, "File system `%s' is of an unknown type",
234 params->filesystem);
235 }
236
237 if (argc >= 2) {
238 params->stage1 = argv[1];
239 if ((params->s1fd = open(params->stage1, O_RDONLY, 0600))
240 == -1)
241 err(1, "Opening primary bootstrap `%s'",
242 params->stage1);
243 if (fstat(params->s1fd, ¶ms->s1stat) == -1)
244 err(1, "Examining primary bootstrap `%s'",
245 params->stage1);
246 if (!S_ISREG(params->s1stat.st_mode))
247 err(1, "`%s' must be a regular file", params->stage1);
248 }
249 if (argc == 3) {
250 params->stage2 = argv[2];
251 }
252 assert(params->machine != NULL);
253
254 if (params->flags & IB_VERBOSE) {
255 printf("File system: %s\n", params->filesystem);
256 printf("File system type: %s (blocksize %u, needswap %d)\n",
257 params->fstype->name,
258 params->fstype->blocksize, params->fstype->needswap);
259 printf("Primary bootstrap: %s\n",
260 (params->flags & IB_CLEAR) ? "(to be cleared)"
261 : params->stage1);
262 if (params->stage2 != NULL)
263 printf("Secondary bootstrap: %s\n", params->stage2);
264 }
265
266 if (params->flags & IB_CLEAR) {
267 op = "Clear";
268 rv = params->machine->clearboot(params);
269 } else {
270 op = "Set";
271 rv = params->machine->setboot(params);
272 }
273 if (rv == 0)
274 errx(1, "%s bootstrap operation failed", op);
275
276 if (S_ISREG(params->fsstat.st_mode)) {
277 if (fsync(params->fsfd) == -1)
278 err(1, "Synchronising file system `%s'",
279 params->filesystem);
280 } else {
281 /* Sync filesystems (to clean in-memory superblock?) */
282 sync();
283 }
284 if (close(params->fsfd) == -1)
285 err(1, "Closing file system `%s'", params->filesystem);
286 if (argc == 2)
287 if (close(params->s1fd) == -1)
288 err(1, "Closing primary bootstrap `%s'",
289 params->stage1);
290
291 exit(0);
292 /* NOTREACHED */
293 }
294
295 static void
296 parseoptions(ib_params *params, const char *option)
297 {
298 char *cp;
299 const struct option *opt;
300 int len;
301 unsigned long val;
302
303 assert(params != NULL);
304 assert(option != NULL);
305
306 for (;; option += len) {
307 option += strspn(option, ", \t");
308 if (*option == 0)
309 return;
310 len = strcspn(option, "=,");
311 for (opt = options; opt->name != NULL; opt++) {
312 if (memcmp(option, opt->name, len) == 0
313 && opt->name[len] == 0)
314 break;
315 }
316 if (opt->name == NULL) {
317 len = strcspn(option, ",");
318 warnx("Unknown option `-o %.*s'", len, option);
319 break;
320 }
321 params->flags |= opt->flag;
322 if (opt->type == OPT_BOOL) {
323 if (option[len] != '=')
324 continue;
325 warnx("Option `%s' must not have a value", opt->name);
326 break;
327 }
328 if (option[len] != '=') {
329 warnx("Option `%s' must have a value", opt->name);
330 break;
331 }
332 option += len + 1;
333 len = strcspn(option, ",");
334 switch (opt->type) {
335 case OPT_STRING:
336 len = strlen(option);
337 /* FALLTHROUGH */
338 case OPT_WORD:
339 cp = strdup(option);
340 if (cp == NULL)
341 err(1, "strdup");
342 cp[len] = 0;
343 OPTION(params, char *, opt) = cp;
344 continue;
345 case OPT_INT:
346 val = strtoul(option, &cp, 0);
347 if (cp > option + len || (*cp != 0 && *cp != ','))
348 break;
349 OPTION(params, int, opt) = val;
350 if (OPTION(params, int, opt) != val)
351 /* value got truncated on int convertion */
352 break;
353 continue;
354 default:
355 errx(1, "Internal error: option `%s' has invalid type %d",
356 opt->name, opt->type);
357 }
358 warnx("Invalid option value `%s=%.*s'", opt->name, len, option);
359 break;
360 }
361 options_usage();
362 exit(1);
363 }
364
365 static void
366 options_usage(void)
367 {
368 int ndx;
369 const char *pfx;
370
371 warnx("Valid options are:");
372 pfx = "\t";
373 for (ndx = 0; options[ndx].name != 0; ndx++) {
374 fprintf(stderr, "%s%s", pfx, options[ndx].name);
375 switch (options[ndx].type) {
376 case OPT_INT:
377 fprintf(stderr, "=number");
378 break;
379 case OPT_WORD:
380 fprintf(stderr, "=word");
381 break;
382 case OPT_STRING:
383 fprintf(stderr, "=string");
384 break;
385 default:
386 break;
387 }
388 if ((ndx % 5) == 4)
389 pfx = ",\n\t";
390 else
391 pfx = ", ";
392 }
393 fprintf(stderr, "\n");
394 }
395
396 int
397 no_setboot(ib_params *params)
398 {
399
400 assert(params != NULL);
401
402 /* bootstrap installation is not supported */
403 warnx("%s: bootstrap installation is not supported",
404 params->machine->name);
405 return (0);
406 }
407
408 int
409 no_clearboot(ib_params *params)
410 {
411
412 assert(params != NULL);
413
414 /* bootstrap removal is not supported */
415 warnx("%s: bootstrap removal is not supported",
416 params->machine->name);
417 return (0);
418 }
419
420
421 static void
422 getmachine(ib_params *param, const char *mach, const char *provider)
423 {
424 int i;
425
426 assert(param != NULL);
427 assert(mach != NULL);
428 assert(provider != NULL);
429
430 for (i = 0; machines[i].name != NULL; i++) {
431 if (strcmp(machines[i].name, mach) == 0) {
432 param->machine = &machines[i];
433 return;
434 }
435 }
436 warnx("Invalid machine `%s' from %s", mach, provider);
437 machine_usage();
438 exit(1);
439 }
440
441 static void
442 machine_usage(void)
443 {
444 const char *prefix;
445 int i;
446
447 warnx("Supported machines are:");
448 #define MACHS_PER_LINE 9
449 prefix="";
450 for (i = 0; machines[i].name != NULL; i++) {
451 if (i == 0)
452 prefix="\t";
453 else if (i % MACHS_PER_LINE)
454 prefix=", ";
455 else
456 prefix=",\n\t";
457 fprintf(stderr, "%s%s", prefix, machines[i].name);
458 }
459 fputs("\n", stderr);
460 }
461
462 static void
463 getfstype(ib_params *param, const char *fstype, const char *provider)
464 {
465 int i;
466
467 assert(param != NULL);
468 assert(fstype != NULL);
469 assert(provider != NULL);
470
471 for (i = 0; fstypes[i].name != NULL; i++) {
472 if (strcmp(fstypes[i].name, fstype) == 0) {
473 param->fstype = &fstypes[i];
474 return;
475 }
476 }
477 warnx("Invalid file system type `%s' from %s", fstype, provider);
478 fstype_usage();
479 exit(1);
480 }
481
482 static void
483 fstype_usage(void)
484 {
485 const char *prefix;
486 int i;
487
488 warnx("Supported file system types are:");
489 #define FSTYPES_PER_LINE 9
490 prefix="";
491 for (i = 0; fstypes[i].name != NULL; i++) {
492 if (i == 0)
493 prefix="\t";
494 else if (i % FSTYPES_PER_LINE)
495 prefix=", ";
496 else
497 prefix=",\n\t";
498 fprintf(stderr, "%s%s", prefix, fstypes[i].name);
499 }
500 fputs("\n", stderr);
501 }
502
503 static void
504 usage(void)
505 {
506 const char *prog;
507
508 prog = getprogname();
509 fprintf(stderr,
510 "usage: %s [-nv] [-m machine] [-o options] [-t fstype]\n"
511 "\t\t [-b s1start] [-B s2start] filesystem primary [secondary]\n"
512 "usage: %s -c [-nv] [-m machine] [-o options] [-t fstype] filesystem\n",
513 prog, prog);
514 machine_usage();
515 fstype_usage();
516 options_usage();
517 exit(1);
518 }
519