diff --git a/cdrom/tbbootconfig/Makefile b/cdrom/tbbootconfig/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f5ea244a520a65ea2aaa42ece05ba4f26f4727c7 --- /dev/null +++ b/cdrom/tbbootconfig/Makefile @@ -0,0 +1,7 @@ +CFLAGS += -I. -g +LDFLAGS += -lz + +tbbootconfig: tbbootconfig.c + +clean: + rm tbbootconfig diff --git a/cdrom/tbbootconfig/tbbootconfig.c b/cdrom/tbbootconfig/tbbootconfig.c new file mode 100644 index 0000000000000000000000000000000000000000..abeda5adc94494b4673221167286373478d8bb44 --- /dev/null +++ b/cdrom/tbbootconfig/tbbootconfig.c @@ -0,0 +1,513 @@ +/* + * EMULAB-COPYRIGHT + * Copyright (c) 2000-2002 University of Utah and the Flux Group. + * All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <sys/param.h> +#include <fcntl.h> +#include <zlib.h> +#include <err.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "testbed_boot.h" + +/* + * Operate on the testbed boot header. + */ +static char *usagestr = + "usage: %s <options> devicefile\n" + " -f Force operation\n" + " -i Info mode only; do not write anything\n" + " -d Turn on debugging.\n" + " -v Verify and exit with status; Do not print anything\n" + " -p Purge the header block (write zeros)\n" + " -w filename Write system config to header block from file\n" + " -r filename Read system config from header block and write to file\n" + " -k 0,1 Set the bootfromdisk flag to either 0 or 1\n" + " -c 0,1 Set the bootfromcdrom flag to either 0 or 1\n" + " -m 0,1 Set the validimage flag to either 0 or 1\n" + " -e string Get/Set the emulab key. Use - to get the key\n" + " device The device special file (ie: /dev/rad0)\n"; + +/* Locals */ +static int info, debug, force, verify, purge; +static char *progname; + +/* Forward decls */ +static void usage(void); +static int readhdr(int devfd, tbboot_t *tbhdr); +static int writehdr(int devfd, tbboot_t *tbhdr); +static int readconfigfromfile(int devfd, tbboot_t *tbhdr, char *path); +static int writeconfigtofile(int devfd, tbboot_t *tbhdr, char *path); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int rval, ch, devfd; + tbboot_t tbboot_hdr; + int bootfromcdrom = -1, bootfromdisk = -1, validimage = -1; + char *configfile = NULL, *emulabkey = NULL; + int readconfig = 0, writeconfig = 0; + + progname = argv[0]; + + if (geteuid()) { + fprintf(stderr, "You must be root!\n"); + exit(-1); + } + + if (sizeof(tbboot_hdr) > SECSIZE) { + warnx("boot header size is too big!\n"); + exit(-1); + } + + while ((ch = getopt(argc, argv, "fidk:c:vm:r:w:pe:")) != -1) + switch(ch) { + case 'i': + info++; + break; + case 'd': + debug++; + break; + case 'f': + force++; + break; + case 'v': + verify++; + break; + case 'p': + purge++; + break; + case 'k': + bootfromdisk = atoi(optarg); + if (bootfromdisk < 0 || bootfromdisk > 1) + usage(); + break; + case 'c': + bootfromcdrom = atoi(optarg); + if (bootfromcdrom < 0 || bootfromcdrom > 1) + usage(); + break; + case 'm': + validimage = atoi(optarg); + if (validimage < 0 || validimage > 1) + usage(); + break; + case 'r': + configfile = optarg; + readconfig = 1; + break; + case 'w': + configfile = optarg; + writeconfig = 1; + break; + case 'e': + emulabkey = optarg; + break; + case 'h': + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + if ((devfd = open(argv[0], (info ? O_RDONLY : O_RDWR))) < 0) { + if (! verify) /* Be silent */ + perror("opening device"); + exit(1); + } + rval = readhdr(devfd, &tbboot_hdr); + if (verify) + exit(rval); + + if (configfile && readconfig) + exit(writeconfigtofile(devfd, &tbboot_hdr, configfile)); + + if (emulabkey && emulabkey[0] == '-') { + if (! strlen(tbboot_hdr.emulabkey)) + exit(-1); + printf("%s\n", tbboot_hdr.emulabkey); + exit(0); + } + + if (info) + exit(rval); + + if (purge) { + char buf[SECSIZE]; + + bzero(buf, sizeof(buf)); + + if (pwrite(devfd, buf, sizeof(buf), (off_t)TBBOOT_OFFSET) < 0){ + perror("purging header"); + exit(1); + } + exit(0); + } + + if (bootfromcdrom != -1) + tbboot_hdr.bootfromcdrom = bootfromcdrom; + if (bootfromdisk != -1) + tbboot_hdr.bootfromdisk = bootfromdisk; + if (validimage != -1) + tbboot_hdr.validimage = bootfromdisk; + + if (emulabkey) { + if (strlen(emulabkey) > TBBOOT_MAXKEYLEN - 1) { + warnx("Key is longer that %d chars!", + TBBOOT_MAXKEYLEN - 1); + exit(-1); + } + strcpy(tbboot_hdr.emulabkey, emulabkey); + } + + if (tbboot_hdr.bootfromdisk == tbboot_hdr.bootfromcdrom && !force) { + warnx("Attempt to set bootfromcdrom=bootfromdisk! " + "Use -f option.\n"); + exit(-1); + } + + if (configfile && writeconfig && + readconfigfromfile(devfd, &tbboot_hdr, configfile)) + exit(-1); + + writehdr(devfd, &tbboot_hdr); + + close(devfd); + exit(0); +} + +/* + * Read and validate the header. Returns -1 if the header is invalid. + */ +static int +readhdr(int devfd, tbboot_t *tbhdr) +{ + char buf[SECSIZE]; + unsigned oldcrc, newcrc; + + if (lseek(devfd, (off_t) TBBOOT_OFFSET, SEEK_SET) < 0) { + perror("seeking to read header"); + exit(1); + } + + if (read(devfd, buf, sizeof(buf)) < 0) { + perror("reading header"); + exit(1); + } + memcpy(tbhdr, buf, sizeof(*tbhdr)); + + if (tbhdr->magic1 != TBBOOT_MAGIC1 || tbhdr->magic2 != TBBOOT_MAGIC2) { + if (verify) + return -1; + + warnx("Bad magic number in header!"); + if (! force) + exit(1); + tbhdr->magic1 = TBBOOT_MAGIC1; + tbhdr->magic2 = TBBOOT_MAGIC2; + tbhdr->version = TBBOOT_VERSION; + + /* No info to print */ + return -1; + } + + if (debug) { + fprintf(stderr, "Reading TB Boot Header:\n"); + fprintf(stderr, " bootfromdisk: %d\n", + tbhdr->bootfromdisk); + fprintf(stderr, " bootfromcdrom: %d\n", + tbhdr->bootfromcdrom); + fprintf(stderr, " checksum: %lx\n", + tbhdr->checksum); + fprintf(stderr, " emulabkey: %s\n", + tbhdr->emulabkey); + fprintf(stderr, " validimage: %d\n", + tbhdr->validimage); + fprintf(stderr, " validconfig: %d\n", + tbhdr->validconfig); + + if (tbhdr->validconfig) { + fprintf(stderr, " interface: %s\n", + tbhdr->sysconfig.interface); + fprintf(stderr, " hostname: %s\n", + tbhdr->sysconfig.hostname); + fprintf(stderr, " domain: %s\n", + tbhdr->sysconfig.domain); + fprintf(stderr, " IP: %s\n", + inet_ntoa(tbhdr->sysconfig.IP)); + fprintf(stderr, " netmask: %s\n", + inet_ntoa(tbhdr->sysconfig.netmask)); + fprintf(stderr, " nameserver: %s\n", + inet_ntoa(tbhdr->sysconfig.nameserver)); + fprintf(stderr, " gateway: %s\n", + inet_ntoa(tbhdr->sysconfig.gateway)); + } + } + + /* + * Check the checksum. + */ + oldcrc = tbhdr->checksum; + tbhdr->checksum = 0; + newcrc = crc32(0L, Z_NULL, 0); + newcrc = crc32(newcrc, (const char *) tbhdr, sizeof(*tbhdr)); + + if (newcrc != oldcrc) { + if (verify) + return -1; + + warnx("Bad crc header! %x != %x\n", newcrc, oldcrc); + if (! force) + exit(1); + return -1; + } + return 0; +} + +/* + * Write the header, + */ +static int +writehdr(int devfd, tbboot_t *tbhdr) +{ + char buf[SECSIZE]; + unsigned long newcrc; + + if (tbhdr->magic1 != TBBOOT_MAGIC1 || tbhdr->magic2 != TBBOOT_MAGIC2) { + warnx("Bad magic number in header!"); + if (! force) + exit(1); + } + tbhdr->checksum = 0; + newcrc = crc32(0L, Z_NULL, 0); + newcrc = crc32(newcrc, (const char *) tbhdr, sizeof(*tbhdr)); + tbhdr->checksum = newcrc; + + if (debug) { + if (info) + fprintf(stderr, "Would write TB Boot Header:\n"); + else + fprintf(stderr, "Writing TB Boot Header:\n"); + + fprintf(stderr, " bootfromdisk: %d\n", + tbhdr->bootfromdisk); + fprintf(stderr, " bootfromcdrom: %d\n", + tbhdr->bootfromcdrom); + fprintf(stderr, " checksum: %lx\n", + tbhdr->checksum); + fprintf(stderr, " emulabkey: %s\n", + tbhdr->emulabkey); + fprintf(stderr, " validimage: %d\n", + tbhdr->validimage); + fprintf(stderr, " validconfig: %d\n", + tbhdr->validconfig); + + if (tbhdr->validconfig) { + fprintf(stderr, " interface: %s\n", + tbhdr->sysconfig.interface); + fprintf(stderr, " hostname: %s\n", + tbhdr->sysconfig.hostname); + fprintf(stderr, " domain: %s\n", + tbhdr->sysconfig.domain); + fprintf(stderr, " IP: %s\n", + inet_ntoa(tbhdr->sysconfig.IP)); + fprintf(stderr, " netmask: %s\n", + inet_ntoa(tbhdr->sysconfig.netmask)); + fprintf(stderr, " nameserver: %s\n", + inet_ntoa(tbhdr->sysconfig.nameserver)); + fprintf(stderr, " gateway: %s\n", + inet_ntoa(tbhdr->sysconfig.gateway)); + } + } + if (info) + return 0; + + bzero(buf, sizeof(buf)); + memcpy(buf, tbhdr, sizeof(*tbhdr)); + + if (pwrite(devfd, buf, sizeof(buf), (off_t) TBBOOT_OFFSET) < 0) { + perror("writing header"); + exit(1); + } + + return 0; +} + +#define CONFIG_STRING 1 +#define CONFIG_INADDR 2 +tbboot_t foohdr; + +struct configval { + char *name; + int type; + int size; + int offset; + union { + char *string; + struct in_addr in; + } value; +} configvals[] = { + {"IP", CONFIG_INADDR, sizeof(foohdr.sysconfig.IP), + (char *) &foohdr.sysconfig.IP - (char *) &foohdr }, + {"netmask", CONFIG_INADDR, sizeof(foohdr.sysconfig.netmask), + (char *) &foohdr.sysconfig.netmask - (char *) &foohdr }, + {"nameserver", CONFIG_INADDR, sizeof(foohdr.sysconfig.nameserver), + (char *) &foohdr.sysconfig.nameserver - (char *) &foohdr }, + {"gateway", CONFIG_INADDR, sizeof(foohdr.sysconfig.gateway), + (char *) &foohdr.sysconfig.gateway - (char *) &foohdr }, + {"interface", CONFIG_STRING, sizeof(foohdr.sysconfig.interface), + (char *) &foohdr.sysconfig.interface - (char *) &foohdr }, + {"hostname", CONFIG_STRING, sizeof(foohdr.sysconfig.hostname), + (char *) &foohdr.sysconfig.hostname - (char *) &foohdr }, + {"domain", CONFIG_STRING, sizeof(foohdr.sysconfig.domain), + (char *) &foohdr.sysconfig.domain - (char *) &foohdr }, +}; +int maxconfigs = sizeof(configvals)/sizeof(struct configval); + +static int +readconfigfromfile(int devfd, tbboot_t *tbhdr, char *path) +{ + FILE *fp; + int i, badconfig = 0; + char buf[BUFSIZ]; + + if ((fp = fopen(path, "r")) == NULL) { + perror("opening configfile for read"); + exit(-1); + } + + while (fgets(buf, sizeof(buf), fp)) { + char *name, *value, *bp; + + if (buf[0] == '\n' || buf[0] == '#') + continue; + + if ((bp = rindex(buf, '\n'))) + *bp = 0; + + if (! (bp = index(buf, '='))) { + warnx("Misformed input line: '%s'\n", buf); + return -1; + } + *bp = NULL; + name = buf; + value = bp + 1; + + if (!strlen(name) || !strlen(value)) { + warnx("Misformed input line: '%s:%s'\n", name, value); + return -1; + } + + for (i = 0; i < maxconfigs; i++) { + if (! strcmp(configvals[i].name, name)) + break; + } + if (i == maxconfigs) { + warnx("Invalid input line: '%s'\n", buf); + return -1; + } + switch (configvals[i].type) { + case CONFIG_STRING: + if (strlen(value) > configvals[i].size - 1) { + warnx("Value too long!: %s\n", value); + return -1; + } + configvals[i].value.string = strdup(value); + break; + + case CONFIG_INADDR: + inet_aton(value, &configvals[i].value.in); + break; + + default: + warnx("Bad config type: %d\n", configvals[i].type); + return -1; + } + } + + /* + * Check to make sure all the fields were specified. If so, we can + * set the sysconfig to valid and overwrite the current info. + */ + for (i = 0; i < maxconfigs; i++) { + if (! configvals[i].value.string) { + warnx("Config is missing: '%s'\n", configvals[i].name); + badconfig++; + } + } + if (badconfig) { + warnx("Not writing configuration because its incomplete.\n"); + return -1; + } + + /* + * Valid config. Change the boot header. + */ + for (i = 0; i < maxconfigs; i++) { + char *foo = ((char *) tbhdr) + configvals[i].offset; + + switch (configvals[i].type) { + case CONFIG_STRING: + strcpy(foo, configvals[i].value.string); + break; + + case CONFIG_INADDR: + memcpy(foo, &configvals[i].value, + sizeof(struct in_addr)); + break; + } + } + tbhdr->validconfig = 1; + + return 0; +} + +static int +writeconfigtofile(int devfd, tbboot_t *tbhdr, char *path) +{ + FILE *fp; + + if (!tbhdr->validconfig) { + warnx("There is no system configuration in the header block!"); + return -1; + } + + if ((fp = fopen(path, "w")) == NULL) { + perror("opening configfile for write"); + exit(-1); + } + + fprintf(fp, "interface=%s\n", tbhdr->sysconfig.interface); + fprintf(fp, "hostname=%s\n", tbhdr->sysconfig.hostname); + fprintf(fp, "domain=%s\n", tbhdr->sysconfig.domain); + fprintf(fp, "IP=%s\n", inet_ntoa(tbhdr->sysconfig.IP)); + fprintf(fp, "netmask=%s\n", inet_ntoa(tbhdr->sysconfig.netmask)); + fprintf(fp, "nameserver=%s\n",inet_ntoa(tbhdr->sysconfig.nameserver)); + fprintf(fp, "gateway=%s\n", inet_ntoa(tbhdr->sysconfig.gateway)); + + fclose(fp); + return 0; +} + +static void +usage() +{ + fprintf(stderr, usagestr, progname); + exit(1); +} + + diff --git a/cdrom/tbbootconfig/testbed_boot.h b/cdrom/tbbootconfig/testbed_boot.h new file mode 100644 index 0000000000000000000000000000000000000000..3fa37a15bd7fc42e6591b250cef3beb5c1baf54a --- /dev/null +++ b/cdrom/tbbootconfig/testbed_boot.h @@ -0,0 +1,111 @@ +/* + * EMULAB-COPYRIGHT + * Copyright (c) 2000-2002 University of Utah and the Flux Group. + * All rights reserved. + */ + +#include <netinet/in.h> + +/* + * This structure is placed in the boot block. The loader reads the + * structure to determine whether it should boot from CDROM or switch + * to the disk. + * + * The basic idea is this: + * + * 1. The node always boots from the CD-ROM (first in boot chain). + * 2. The loader looks for this magic structure: + * 2a: If not present continues to load from the CD-ROM (step 4). + * 2b: If present check the contents of the structure. If directed to + * boot from the disk goto step 3. If not, continues to load from + * the CDROM (step 4). + * 3. Boot from the disk. Switch the boot device to the disk so that the + * kernel is loaded from the disk. Also reset the flag (written to disk) + * to indicate that next reboot should happen from the CDROM. Also set + * another flag, cleared by the kernel, that indicates the kernel booted + * okay. This avoids reboot loops in the case of a scrogged disk. + * 4. Boot from the CD-ROM. The user level code on the CD-ROM will take care + * of the rest, writing the proper flags to the structure on the disk. + * Normally, this means checking in with Emulab, and then setting the flag + * to cause it to boot from the disk. + * + * This entire structure has to be less than a sector. + */ +#define SECSIZE 512 +#define TBBOOT_MAXIFACELEN 12 +#define TBBOOT_MAXHOSTLEN 32 +#define TBBOOT_MAXDOMAINLEN 64 +#define TBBOOT_MAXKEYLEN 64 +#define TBBOOT_MAXDISKDEVLEN 32 + +typedef struct tbboot_header +{ + unsigned long magic1; + short version; + + /* + * Set bootfromdisk to 1 to force loader to boot from disk. + */ + char bootfromdisk; + + /* + * Set bootfromcdrom to 0 when booting from the disk. The kernel + * will set this to 1. If the cdrom boots with bootfromdisk 0 + * and bootfromcdrom 0, something went wrong and the kernel did not + * boot properly. Avoids a loop. + */ + char bootfromcdrom; + + /* + * Flag to indicate the image is valid. Clear this when writing + * a new image, and set it when done. + */ + char validimage; + + /* + * Flag to indicate the system config block is valid (has info). + */ + char validconfig; + + /* + * crc32 from the zlib library. + */ + unsigned long checksum; + + /* Paranoia */ + unsigned long magic2; + + /* + * The emulab key. + */ + char emulabkey[TBBOOT_MAXKEYLEN]; + + /* + * System configuration. + */ + struct { + char interface[TBBOOT_MAXIFACELEN]; + char hostname[TBBOOT_MAXHOSTLEN]; + char domain[TBBOOT_MAXDOMAINLEN]; + struct in_addr IP; + struct in_addr netmask; + struct in_addr nameserver; + struct in_addr gateway; + } sysconfig; +} tbboot_t; + +/* Magic value identifying the header. */ +#define TBBOOT_MAGIC1 0x9badbeef +#define TBBOOT_MAGIC2 0x69ceafd8 + +/* Current Version */ +#define TBBOOT_VERSION 100 + +/* + * Offset from start of the disk. Hardwired to sector 60 which should be + * clear on our images. + */ +#define TBBOOT_SECTOR 60 +#define TBBOOT_OFFSET (TBBOOT_SECTOR * SECSIZE) + +