/*
 * init.c
 * 
 * This is the install type init 
 *
 * Erik Troan (ewt@redhat.com)
 *
 * Copyright 1996 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.
 *
 */

#include <fcntl.h>
#include <linux/fs.h>
#include <linux/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>

#ifdef __alpha__
#include <sys/reboot.h>
#endif

/* 
 * 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 doShutdown;

#define __LIBRARY__
#include <linux/unistd.h>
#ifndef __alpha__
# define __NR_sys_syslog __NR_syslog
_syscall3(int,syslog,int, type, char *, buf, int, len);
#else
# define syslog klogctl
#endif

void fatal_error(int usePerror) {
    if (usePerror) 
	perror("failed:");
    else
	fprintf(stderr, "failed.\n");

    fprintf(stderr, "\nI can't recover from this.\n");
    while (1) ;
}

int doMke2fs(char * device, char * size) {
    char * args[] = { "/usr/bin/mke2fs", NULL, NULL, NULL };
    int pid, status;

    args[1] = device;
    args[2] = size;

    if (!(pid = fork())) {
	execv("/usr/bin/mke2fs", args);
	fatal_error(1);
    }

    wait(&status);
    
    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) {
	perror("socket");
	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) {
	    perror("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;
    size_t s;
    int sock;
    struct sockaddr_un sockaddr;
    char buf[1024];
    int readfd;

    if (fork())
	return;

    in = open("/proc/kmsg", O_RDONLY);
    if (in < 0) {
	perror("open /proc/kmsg");
	return;
    }

    out = open(fn, O_WRONLY);
    if (out < 0) {
	close(in);
	perror("open kmsg log");
	return;
    }

    /* 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 < 1) {
	write(out, "socket error\n", 13);
    }
    if (bind(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr.sun_family) + 
			strlen(sockaddr.sun_path))) {
	write(out, "bind error\n", 11);
    }
    chmod("/dev/log", 0666);
    if (listen(sock, 5)) {
	write(out, "listen error\n", 13);
    }
    syslog(8, NULL, 1);

    FD_ZERO(&unixs);
    while (1) {
	memcpy(&readset, &unixs, sizeof(unixs));

	FD_SET(sock, &readset);
	FD_SET(in, &readset);

	i = select(FD_SETSIZE, &readset, NULL, NULL, NULL);
	if (i <= 0) continue;

	if (FD_ISSET(in, &readset)) {
	    i = read(in, buf, sizeof(buf));
	    if (i > 0) write(out, buf, i);
	} 

	for (readfd = 0; readfd < FD_SETSIZE; ++readfd) {
	    if (FD_ISSET(readfd, &readset) && FD_ISSET(readfd, &unixs)) {
		i = read(readfd, buf, sizeof(buf));
		if (i > 0) {
		    write(out, buf, i);
		    write(out, "\n", 1);
		} else if (i == 0) {
		    /* socket closed */
		    close(readfd);
		    FD_CLR(readfd, &unixs);
		}
	    }
	}

	if (FD_ISSET(sock, &readset)) {
	    i = sizeof(sockaddr);
	    readfd = accept(sock, (struct sockaddr *) &sockaddr, &s);
	    if (readfd < 0) {
		/*write(out, "error in accept\n", 16);*/
	    } else {
		FD_SET(readfd, &unixs);
	    }
	}
    }    
}

#if defined(__alpha__)
char * findKernel(void) {
    char * dev, * file;
    struct stat sb;

    dev = getenv("bootdevice");
    file = getenv("bootfile");

    if (!dev || !file) {
	printf("I can't find your kernel. When you are booting"
		" from a CDROM, you must pass\n");
	printf("the bootfile argument to the kernel if your"
		" boot loader doesn't do so automatically.\n");
	printf("\n");
	printf("You should now reboot your system and try "	
		"again\n");

	while (1) ;
    }

    if (!strcmp(dev, "fd0")) {
	if (!strcmp(file, "vmlinux.gz")) {
	    printf("The kernel on a boot floppy must be named vmlinux.gz. "
	           "You\n");
	    printf("are using a kernel named %s instead. You'll have "
		   "to\n", file);
	    printf("fix this and try again.\n");

	    while (1) ;
	}

	return NULL;
    } else {
	if (stat(file, &sb)) {
	    printf("I can't find your kernel. When you are booting"
		    " from a CDROM, you must pass\n");
	    printf("the bootfile argument to the kernel if your"
		    " boot loader doesn't do so automatically.\n");
	    printf("\n");
	    printf("You should now reboot your system and try "	
		    "again\n");

	    while (1) ;
	}

	return file;
    }
}
#endif

void main(void) {
    pid_t installpid, childpid;
    int waitStatus;
    int fd;
    int nfsRoot = 0;
    int roRoot = 0;
    int cdRoot = 0;
    char * kernel;

    /* turn off screen blanking */
    printf("\033[9;0]");
    printf("\033[8]");

    printf("Red Hat install init version %s starting\n", VERSION);

    putenv("PATH=/usr/bin:/bin:/sbin:/usr/sbin:/mnt/sbin:/mnt/usr/sbin:"
		   "/mnt/bin:/mnt/usr/bin");
    putenv("LD_LIBRARY_PATH=/mnt/lib:/mnt/usr/lib");

    sethostname("localhost", 9);

    printf("mounting /proc filesystem... "); fflush(stdout);
    if (mount("/proc", "/proc", "proc", 0, NULL))
	fatal_error(1);

    printf("done\n");

    doklog("/dev/tty4");
 
    printf("checking for NFS root filesystem..."); fflush(stdout);
    if (hasNetConfiged()) {
	printf("yes\n");
	roRoot = nfsRoot = 1;
    } else {
	printf("no\n");
    }

    if (!nfsRoot) {
	printf("trying to remount root filesystem read write... ");
	if (mount("/", "/", NULL, 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");
	    }
	}
    }

    if (roRoot) {
	printf("creating 100k of ramdisk space... "); fflush(stdout);
	if (doMke2fs("/dev/ram", "100"))
	    fatal_error(0);

	printf("done\n");
	
	printf("mounting /tmp from ramdisk... "); fflush(stdout);
	if (mount("/dev/ram", "/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. */

    /* 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");
    if (!(installpid = fork())) {
	if (nfsRoot) {
	    symlink("/", "/tmp/rhimage");
	    symlink("/", "/tmp/image");
	    execl("/usr/bin/runinstall2", "/usr/bin/runinstall2", "--method",
		  "nfs", NULL);
	} else if (cdRoot) {
	    symlink("/", "/tmp/rhimage");
	    symlink("/", "/tmp/image");

#	    if defined(__alpha__)
		kernel = findKernel();

		if (kernel) 
		    execl("/usr/bin/runinstall2", "/usr/bin/runinstall2", 
				"--method", "cdrom", "--kernel", kernel, NULL);
		else
		    execl("/usr/bin/runinstall2", "/usr/bin/runinstall2", 
				"--method", "cdrom", NULL);
#	    else
		execl("/usr/bin/runinstall2", "/usr/bin/runinstall2", 
			    "--method", "cdrom", NULL);
#	    endif
	} else {
	    execl("/bin/install", "/bin/install", NULL);
	}

	exit(0);
    }

    while (!doShutdown) {
	childpid = wait(&waitStatus);

	if (childpid == installpid) 
	    doShutdown = 1;
    }

    sync(); sync();

    printf("rebooting system\n");

    /* this should unmount filesystems, kill processes, etc */
    sleep(2);

    #ifdef __alpha__
	reboot(RB_AUTOBOOT);
    #else
	reboot(0xfee1dead, 672274793, 0x1234567);
    #endif
}
