diff options
author | Jeremy Katz <katzj@redhat.com> | 2002-11-26 19:59:19 +0000 |
---|---|---|
committer | Jeremy Katz <katzj@redhat.com> | 2002-11-26 19:59:19 +0000 |
commit | 32d18cdb5603ba71a27661cc59d78bfaf43a36cc (patch) | |
tree | cbef628263c93aba4f3dbb99573f0dc538db714f /loader2/init.c | |
parent | 48ad552b63a95aec286aba9cd91ec0a745152e95 (diff) | |
download | anaconda-32d18cdb5603ba71a27661cc59d78bfaf43a36cc.tar.gz anaconda-32d18cdb5603ba71a27661cc59d78bfaf43a36cc.tar.xz anaconda-32d18cdb5603ba71a27661cc59d78bfaf43a36cc.zip |
restructuring and clean up of some of the older unused code in the loader.
still a lot to do, but this gets to stage2 for cd, nfs, http, and ftp booting
from either the cd initrd or the pxe initrd
Diffstat (limited to 'loader2/init.c')
-rw-r--r-- | loader2/init.c | 833 |
1 files changed, 833 insertions, 0 deletions
diff --git a/loader2/init.c b/loader2/init.c new file mode 100644 index 000000000..d31e9742f --- /dev/null +++ b/loader2/init.c @@ -0,0 +1,833 @@ +/* + * init.c + * + * This is the install type init + * + * Erik Troan (ewt@redhat.com) + * + * Copyright 1996 - 2002 Red Hat Software + * + * This software may be freely redistributed under the terms of the GNU + * public license. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#if USE_MINILIBC +#include "minilibc.h" +#ifndef SOCK_STREAM +# define SOCK_STREAM 1 +#endif +#else +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <net/if.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/klog.h> +#include <sys/mount.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/swap.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/reboot.h> +#include <termios.h> + +/* Need to tell loop.h what the actual dev_t type is. */ +#undef dev_t +#if defined(__alpha) || (defined(__sparc__) && defined(__arch64__)) +#define dev_t unsigned int +#else +#define dev_t unsigned short +#endif +#include <linux/loop.h> +#undef dev_t +#define dev_t dev_t + +#define syslog klogctl +#endif + +struct unmountInfo { + char * name; + int mounted; + int loopDevice; + enum { FS, LOOP } what; +} ; + +#include <linux/cdrom.h> + +#define KICK_FLOPPY 1 +#define KICK_BOOTP 2 + +#ifndef MS_REMOUNT +#define MS_REMOUNT 32 +#endif + +#define ENV_PATH 0 +#define ENV_LD_LIBRARY_PATH 1 +#define ENV_HOME 2 +#define ENV_TERM 3 +#define ENV_DEBUG 4 + +char * env[] = { + "PATH=/usr/bin:/bin:/sbin:/usr/sbin:/mnt/sysimage/bin:" + "/mnt/sysimage/usr/bin:/mnt/sysimage/usr/sbin:/mnt/sysimage/sbin", + /* we set a nicer ld library path specifically for bash -- a full + one makes anaconda unhappy */ + "LD_LIBRARY_PATH=/lib:/usr/lib:/usr/X11R6/lib", + "HOME=/", + "TERM=linux", + "DEBUG=", + "TERMINFO=/etc/linux-terminfo", + "PYTHONPATH=/tmp/updates", + NULL +}; + + +/* + * this needs to handle the following cases: + * + * 1) run from a CD root filesystem + * 2) run from a read only nfs rooted filesystem + * 3) run from a floppy + * 4) run from a floppy that's been loaded into a ramdisk + * + */ + +int testing=0; + +void printstr(char * string) { + write(1, string, strlen(string)); +} + +void fatal_error(int usePerror) { +/* FIXME */ +#if 0 + if (usePerror) + perror("failed:"); + else +#endif + printf("failed.\n"); + + printf("\nI can't recover from this.\n"); + if (testing) exit(0); +#if !defined(__s390__) && !defined(__s390x__) + while (1) ; +#endif +} + +int doMke2fs(char * device, char * size) { + char * args[] = { "/usr/sbin/mke2fs", NULL, NULL, NULL }; + int pid, status; + + args[1] = device; + args[2] = size; + + if (!(pid = fork())) { + /* child */ + execve("/usr/sbin/mke2fs", args, env); + fatal_error(1); + } + + wait4(-1, &status, 0, NULL); + + return 0; +} + +int hasNetConfiged(void) { + int rc; + int s; + struct ifconf configs; + struct ifreq devs[10]; + + #ifdef __i386__ + return 0; + #endif + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + /* FIXME was perror*/ + printf("error creating socket: %d\n", errno); + return 0; + } else { + /* this is just good enough to tell us if we have anything + configured */ + configs.ifc_len = sizeof(devs); + configs.ifc_buf = (void *) devs; + + rc = ioctl(s, SIOCGIFCONF, &configs); + if (rc < 0) { + /* FIXME was perror*/ + printstr("SIOCGIFCONF"); + return 0; + } + if (configs.ifc_len == 0) { + return 0; + } + + return 1; + } + + return 0; +} + +void doklog(char * fn) { + fd_set readset, unixs; + int in, out, i; + int log; + int s; + int sock = -1; + struct sockaddr_un sockaddr; + char buf[1024]; + int readfd; + + in = open("/proc/kmsg", O_RDONLY,0); + if (in < 0) { + /* FIXME: was perror */ + printstr("open /proc/kmsg"); + return; + } + + out = open(fn, O_WRONLY, 0); + if (out < 0) + printf("couldn't open %s for syslog -- still using /tmp/syslog\n", fn); + + log = open("/tmp/syslog", O_WRONLY | O_CREAT, 0644); + if (log < 0) { + /* FIXME: was perror */ + printstr("error opening /tmp/syslog"); + sleep(5); + + close(in); + return; + } + + /* if we get this far, we should be in good shape */ + + if (fork()) { + /* parent */ + close(in); + close(out); + close(log); + return; + } + close(0); + close(1); + close(2); + + dup2(1, log); + +#if defined(USE_LOGDEV) + /* now open the syslog socket */ + sockaddr.sun_family = AF_UNIX; + strcpy(sockaddr.sun_path, "/dev/log"); + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + printf("error creating socket: %d\n", errno); + sleep(5); + } + printstr("got socket\n"); + if (bind(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr.sun_family) + + strlen(sockaddr.sun_path))) { + printf("bind error: %d\n", errno); + sleep(5); + } + printstr("bound socket\n"); + chmod("/dev/log", 0666); + if (listen(sock, 5)) { + printf("listen error: %d\n", errno); + sleep(5); + } +#endif + + syslog(8, NULL, 1); + + FD_ZERO(&unixs); + while (1) { + memcpy(&readset, &unixs, sizeof(unixs)); + + if (sock >= 0) FD_SET(sock, &readset); + FD_SET(in, &readset); + + i = select(20, &readset, NULL, NULL, NULL); + if (i <= 0) continue; + + if (FD_ISSET(in, &readset)) { + i = read(in, buf, sizeof(buf)); + if (i > 0) { + if (out >= 0) write(out, buf, i); + write(log, buf, i); + } + } + + for (readfd = 0; readfd < 20; ++readfd) { + if (FD_ISSET(readfd, &readset) && FD_ISSET(readfd, &unixs)) { + i = read(readfd, buf, sizeof(buf)); + if (i > 0) { + if (out >= 0) { + write(out, buf, i); + write(out, "\n", 1); + } + + write(log, buf, i); + write(log, "\n", 1); + } else if (i == 0) { + /* socket closed */ + close(readfd); + FD_CLR(readfd, &unixs); + } + } + } + + if (sock >= 0 && FD_ISSET(sock, &readset)) { + s = sizeof(sockaddr); + readfd = accept(sock, (struct sockaddr *) &sockaddr, &s); + if (readfd < 0) { + if (out >= 0) write(out, "error in accept\n", 16); + write(log, "error in accept\n", 16); + close(sock); + sock = -1; + } else { + FD_SET(readfd, &unixs); + } + } + } +} + +int setupTerminal(int fd) { + struct winsize winsize; + + if (ioctl(fd, TIOCGWINSZ, &winsize)) { + printf("failed to get winsize"); + fatal_error(1); + } + + winsize.ws_row = 24; + winsize.ws_col = 80; + + if (ioctl(fd, TIOCSWINSZ, &winsize)) { + printf("failed to set winsize"); + fatal_error(1); + } + + env[ENV_TERM] = "TERM=vt100"; + + return 0; +} + +void undoLoop(struct unmountInfo * fs, int numFs, int this); + +void undoMount(struct unmountInfo * fs, int numFs, int this) { + int len = strlen(fs[this].name); + int i; + + if (!fs[this].mounted) return; + fs[this].mounted = 0; + + /* unmount everything underneath this */ + for (i = 0; i < numFs; i++) { + if (fs[i].name[len] == '/' && + !strncmp(fs[this].name, fs[i].name, len)) { + if (fs[i].what == LOOP) + undoLoop(fs, numFs, i); + else + undoMount(fs, numFs, i); + } + } + + printf("\t%s", fs[this].name); + /* don't need to unmount /tmp. it is busy anyway. */ + if (!testing) { + if (umount2(fs[this].name, 0) < 0) { + printf(" umount failed (%d)", errno); + } else { + printf(" done"); + } + } + printf("\n"); +} + +void undoLoop(struct unmountInfo * fs, int numFs, int this) { + int i; + int fd; + + if (!fs[this].mounted) return; + fs[this].mounted = 0; + + /* find the device mount */ + for (i = 0; i < numFs; i++) { + if (fs[i].what == FS && (fs[i].loopDevice == fs[this].loopDevice)) + break; + } + + if (i < numFs) { + /* the device is mounted, unmount it (and recursively, anything + * underneath) */ + undoMount(fs, numFs, i); + } + + unlink("/tmp/loop"); + mknod("/tmp/loop", 0600 | S_IFBLK, (7 << 8) | fs[this].loopDevice); + printf("\tdisabling /dev/loop%d", fs[this].loopDevice); + if ((fd = open("/tmp/loop", O_RDONLY, 0)) < 0) { + printf(" failed to open device: %d", errno); + } else { + if (!testing && ioctl(fd, LOOP_CLR_FD, 0)) + printf(" LOOP_CLR_FD failed: %d", errno); + close(fd); + } + + printf("\n"); +} + +void unmountFilesystems(void) { + int fd, size; + char buf[65535]; /* this should be big enough */ + char * chptr, * start; + struct unmountInfo filesystems[500]; + int numFilesystems = 0; + int i; + struct loop_info li; + char * device; + struct stat sb; + + fd = open("/proc/mounts", O_RDONLY, 0); + if (fd < 1) { + /* FIXME: was perror */ + printstr("failed to open /proc/mounts"); + sleep(2); + return; + } + + size = read(fd, buf, sizeof(buf) - 1); + buf[size] = '\0'; + + close(fd); + + chptr = buf; + while (*chptr) { + device = chptr; + while (*chptr != ' ') chptr++; + *chptr++ = '\0'; + start = chptr; + while (*chptr != ' ') chptr++; + *chptr++ = '\0'; + + if (strcmp(start, "/") && strcmp(start, "/tmp")) { + filesystems[numFilesystems].name = alloca(strlen(start) + 1); + strcpy(filesystems[numFilesystems].name, start); + filesystems[numFilesystems].what = FS; + filesystems[numFilesystems].mounted = 1; + + stat(start, &sb); + if ((sb.st_dev >> 8) == 7) { + filesystems[numFilesystems].loopDevice = sb.st_dev & 0xf; + } else { + filesystems[numFilesystems].loopDevice = -1; + } + + numFilesystems++; + } + + while (*chptr != '\n') chptr++; + chptr++; + } + + for (i = 0; i < 7; i++) { + unlink("/tmp/loop"); + mknod("/tmp/loop", 0600 | S_IFBLK, (7 << 8) | i); + if ((fd = open("/tmp/loop", O_RDONLY, 0)) >= 0) { + if (!ioctl(fd, LOOP_GET_STATUS, &li) && li.lo_name[0]) { + filesystems[numFilesystems].name = alloca(strlen(li.lo_name) + + 1); + strcpy(filesystems[numFilesystems].name, li.lo_name); + filesystems[numFilesystems].what = LOOP; + filesystems[numFilesystems].mounted = 1; + filesystems[numFilesystems].loopDevice = i; + numFilesystems++; + } + + close(fd); + } + } + + for (i = 0; i < numFilesystems; i++) { + if (filesystems[i].what == LOOP) { + undoLoop(filesystems, numFilesystems, i); + } + } + + for (i = 0; i < numFilesystems; i++) { + if (filesystems[i].mounted) { + undoMount(filesystems, numFilesystems, i); + } + } +} + +void disableSwap(void) { + int fd; + char buf[4096]; + int i; + char * start; + char * chptr; + + if ((fd = open("/proc/swaps", O_RDONLY, 0)) < 0) return; + + i = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (i < 0) return; + buf[i] = '\0'; + + start = buf; + while (*start) { + while (*start != '\n' && *start) start++; + if (!*start) return; + + start++; + if (*start != '/') return; + chptr = start; + while (*chptr && *chptr != ' ') chptr++; + if (!(*chptr)) return; + *chptr = '\0'; + printf("\t%s", start); + if (swapoff(start)) + printf(" failed (%d)", errno); + printf("\n"); + + start = chptr + 1; + } +} + +void ejectCdrom(void) { + int ejectfd; + struct stat sb; + + stat("/tmp/cdrom", &sb); + + if ((sb.st_mode & S_IFBLK) == S_IFBLK) { + printf("ejecting /tmp/cdrom..."); + if ((ejectfd = open("/tmp/cdrom", O_RDONLY | O_NONBLOCK, 0)) >= 0) { + if (ioctl(ejectfd, CDROMEJECT, 0)) + printf("eject failed %d ", errno); + close(ejectfd); + } else { + printf("eject failed %d ", errno); + } + printf("\n"); + } +} + +int mystrstr(char *str1, char *str2) { + char *p; + int rc=0; + + for (p=str1; *p; p++) { + if (*p == *str2) { + char *s, *t; + + rc = 1; + for (s=p, t=str2; *s && *t; s++, t++) + if (*s != *t) { + rc = 0; + p++; + } + + if (rc) + return rc; + } + } + return rc; +} + + + +int main(int argc, char **argv) { + pid_t installpid, childpid; + int waitStatus; + int fd; + int nfsRoot = 0; + int roRoot = 0; + int cdRoot = 0; + int doReboot = 0; + int doShutdown =0; + int isSerial = 0; + int noKill = 0; +#ifdef __alpha__ + char * kernel; +#endif + char * argvc[15]; + char ** argvp = argvc; + char twelve = 12; + int i; + char buf[500]; + int len; + + +#if !defined(__s390__) && !defined(__s390x__) + testing = (getppid() != 0) && (getppid() != 1); +#endif + + if (!testing) { + /* turn off screen blanking */ + printstr("\033[9;0]"); + printstr("\033[8]"); + } else { + printstr("(running in test mode).\n"); + } + + umask(022); + + printstr("Greetings.\n"); + + printf("Red Hat install init version %s starting\n", VERSION); + + printf("mounting /proc filesystem... "); + if (!testing) { + if (mount("/proc", "/proc", "proc", 0, NULL)) + fatal_error(1); + } + printf("done\n"); + + printf("mounting /dev/pts (unix98 pty) filesystem... "); + if (!testing) { + if (mount("/dev/pts", "/dev/pts", "devpts", 0, NULL)) + fatal_error(1); + } + printf("done\n"); + + signal(SIGINT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + + /* these args are only for testing from commandline */ + for (i = 1; i < argc; i++) { + if (!strcmp (argv[i], "serial")) { + isSerial = 1; + break; + } + } + + /* look through /proc/cmdline for special options */ + if ((fd = open("/proc/cmdline", O_RDONLY,0)) > 0) { + len = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (len > 0 && mystrstr(buf, "nokill")) + noKill = 1; + } + +#if !defined(__s390__) && !defined(__s390x__) + if (ioctl (0, TIOCLINUX, &twelve) < 0) { + isSerial = 2; + } + + if (isSerial) { + char *device = "/dev/ttyS0"; + + printf("Red Hat install init version %s using a serial console\n", + VERSION); + + printf("remember, cereal is an important part of a nutritionally " + "balanced breakfast.\n\n"); + + if (isSerial == 2) + device = "/dev/console"; + fd = open(device, O_RDWR, 0); + if (fd < 0) + device = "/dev/tts/0"; + + if (fd < 0) { + printf("failed to open %s\n", device); + fatal_error(1); + } + + setupTerminal(fd); + } else { + fd = open("/dev/tty1", O_RDWR, 0); + if (fd < 0) + fd = open("/dev/vc/1", O_RDWR, 0); + + if (fd < 0) { + printf("failed to open /dev/tty1 and /dev/vc/1"); + fatal_error(1); + } + } + + if (testing) + exit(0); + + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); +#endif + + setsid(); + if (ioctl(0, TIOCSCTTY, NULL)) { + printf("could not set new controlling tty\n"); + } + + if (!testing) { + sethostname("localhost.localdomain", 21); + /* the default domainname (as of 2.0.35) is "(none)", which confuses + glibc */ + setdomainname("", 0); + } + + printf("checking for NFS root filesystem..."); + if (hasNetConfiged()) { + printf("yes\n"); + roRoot = nfsRoot = 1; + } else { + printf("no\n"); + } + + if (!nfsRoot) { + printf("trying to remount root filesystem read write... "); + if (mount("/", "/", "ext2", MS_REMOUNT | MS_MGC_VAL, NULL)) { + printf("failed (but that's okay)\n"); + + roRoot = 1; + } else { + printf("done\n"); + + /* 2.0.18 (at least) lets us remount a CD r/w!! */ + printf("checking for writeable /tmp... "); + fd = open("/tmp/tmp", O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + printf("no (probably a CD rooted install)\n"); + roRoot = 1; + } else { + close(fd); + unlink("/tmp/tmp"); + printf("yes\n"); + } + } + } + + /* JKFIXME: bah, I don't like this but it has to stay like this until + * ramfs doesn't suck */ +#if !defined(__s390__) && !defined(__s390x__) +#define RAMDISK_DEVICE "/dev/ram" +#else +#define RAMDISK_DEVICE "/dev/ram2" +#endif + + if (!testing && roRoot) { + printf("creating 300k of ramdisk space... "); + if (doMke2fs(RAMDISK_DEVICE, "300")) + fatal_error(0); + + printf("done\n"); + + printf("mounting /tmp from ramdisk... "); + if (mount(RAMDISK_DEVICE, "/tmp", "ext2", 0, NULL)) + fatal_error(1); + + printf("done\n"); + + if (!nfsRoot) cdRoot = 1; + } + + /* Now we have some /tmp space set up, and /etc and /dev point to + it. We should be in pretty good shape. */ + + if (!testing) + doklog("/dev/tty4"); + + /* Go into normal init mode - keep going, and then do a orderly shutdown + when: + + 1) /bin/install exits + 2) we receive a SIGHUP + */ + + printf("running install...\n"); + + setsid(); + + if (!(installpid = fork())) { + /* child */ + *argvp++ = "/sbin/loader"; + *argvp++ = NULL; + + printf("running %s\n", argvc[0]); + execve(argvc[0], argvc, env); + + exit(0); + } + + while (!doShutdown) { + childpid = wait4(-1, &waitStatus, 0, NULL); + + if (childpid == installpid) + doShutdown = 1; + } + + if (!WIFEXITED(waitStatus) || WEXITSTATUS(waitStatus)) { + printf("install exited abnormally "); + if (WIFSIGNALED(waitStatus)) { + printf("-- received signal %d", WTERMSIG(waitStatus)); + } + printf("\n"); + } else { + doReboot = 1; + } + + if (testing) + exit(0); + + sync(); sync(); + + if (!testing && !noKill) { + printf("sending termination signals..."); + kill(-1, 15); + sleep(2); + printf("done\n"); + + printf("sending kill signals..."); + kill(-1, 9); + sleep(2); + printf("done\n"); + } + + printf("disabling swap...\n"); + disableSwap(); + + printf("unmounting filesystems...\n"); + unmountFilesystems(); + + ejectCdrom(); + + if (doReboot) { + printf("rebooting system\n"); + sleep(2); + +#if USE_MINILIBC + reboot(0xfee1dead, 672274793, 0x1234567); +#else +# ifdef __alpha__ + reboot(RB_HALT_SYSTEM); +# else + reboot(RB_AUTOBOOT); +# endif +#endif + } else { + printf("you may safely reboot your system\n"); + while (1); + } + + exit(0); + + return 0; +} |