Initial revision
diff --git a/init/init.c b/init/init.c
new file mode 100644
index 0000000..4771722
--- /dev/null
+++ b/init/init.c
@@ -0,0 +1,438 @@
+#include "internal.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/kdaemon.h>
+#include <sys/swap.h>
+#include <sys/sysmacros.h>
+
+const char		init_usage[] = "Used internally by the system.";
+char			console[16] = "";
+const char *	default_console = "/dev/tty1";
+char *			first_terminal = NULL;
+const char *	second_terminal = "/dev/tty2";
+const char		log[] = "/dev/tty3";
+char * term_ptr = NULL;
+
+static void
+message(const char * terminal, const char * pattern, ...)
+{
+	int	fd;
+	FILE *	con = 0;
+	va_list	arguments;
+
+	/*
+	 * Open the console device each time a message is printed. If the user
+	 * has switched consoles, the open will get the new console. If we kept
+	 * the console open, we'd always print to the same one.
+	 */
+	if ( ((fd = open(terminal, O_WRONLY|O_NOCTTY)) < 0)
+	 ||  ((con = fdopen(fd, "w")) == NULL) )
+		return;
+
+	va_start(arguments, pattern);
+	vfprintf(con, pattern, arguments);
+	va_end(arguments);
+	fclose(con);
+}
+
+static int
+waitfor(int pid)
+{
+	int	status;
+	int	wpid;
+	
+	message(log, "Waiting for process %d.\n", pid);
+	while ( (wpid = wait(&status)) != pid ) {
+		if ( wpid > 0 ) {
+			message(
+			 log
+			,"pid %d exited, status=%x.\n"
+			,wpid
+			,status);
+		}
+	}
+	return wpid;
+}
+
+static int
+run(
+ const char *			program
+,const char * const *	arguments
+,const char *			terminal
+,int					get_enter)
+{
+	static const char	control_characters[] = {
+		'\003',
+		'\034',
+		'\177',
+		'\025',
+		'\004',
+		'\0',
+		'\1',
+		'\0',
+		'\021',
+		'\023',
+		'\032',
+		'\0',
+		'\022',
+		'\017',
+		'\027',
+		'\026',
+		'\0'
+	};
+
+	static char * environment[] = {
+		"HOME=/",
+		"PATH=/bin:/sbin:/usr/bin:/usr/sbin",
+		"SHELL=/bin/sh",
+		0,
+		"USER=root",
+		0
+	};
+
+	static const char	press_enter[] =
+ "Please press Enter to activate this console. ";
+
+	int	pid;
+
+	environment[3]=term_ptr;
+
+	pid = fork();
+	if ( pid == 0 ) {
+		struct termios	t;
+		const char * const * arg;
+
+		close(0);
+		close(1);
+		close(2);
+		setsid();
+
+		open(terminal, O_RDWR);
+		dup(0);
+		dup(0);
+		tcsetpgrp(0, getpgrp());
+
+		tcgetattr(0, &t);
+		memcpy(t.c_cc, control_characters, sizeof(control_characters));
+		t.c_line = 0;
+		t.c_iflag = ICRNL|IXON|IXOFF;
+		t.c_oflag = OPOST|ONLCR;
+		t.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN;
+		tcsetattr(0, TCSANOW, &t);
+
+		if ( get_enter ) {
+			/*
+			 * Save memory by not exec-ing anything large (like a shell)
+			 * before the user wants it. This is critical if swap is not
+			 * enabled and the system has low memory. Generally this will
+			 * be run on the second virtual console, and the first will
+			 * be allowed to start a shell or the installation system.
+			 */
+			char	c;
+			write(1, press_enter, sizeof(press_enter) - 1);
+			read(0, &c, 1);
+		}
+		
+		message(log, "Executing ");
+		arg = arguments;
+		while ( *arg != 0 )
+			message(log, "%s ", *arg++);
+		message(log, "\n");
+
+		execve(program, (char * *)arguments, (char * *)environment);
+		message(log, "%s: could not execute: %s.\r\n", program, strerror(errno));
+		exit(-1);
+	}
+	return pid;
+}
+
+static int
+mem_total()
+{
+  char s[80];
+  char *p;
+  FILE *f;
+  const char pattern[]="MemTotal:";
+
+  f=fopen("/proc/meminfo","r");
+  while (NULL != fgets(s,79,f)) {
+    p=strstr(s, pattern);
+    if (NULL != p) {
+      fclose(f);
+      return(atoi(p+strlen(pattern)));
+    }
+  }
+  return -1;
+}
+
+static void
+set_free_pages()
+{
+  char s[80];
+  FILE *f;
+
+  f=fopen("/proc/sys/vm/freepages","r");
+  fgets(s,79,f);
+  if (atoi(s) < 32) {
+    fclose(f);
+    f=fopen("/proc/sys/vm/freepages","w");
+    fprintf(f,"30\t40\t50\n");
+    printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n");
+  }
+  fclose(f);
+}
+
+static void
+shutdown_system(int do_reboot)
+{
+	static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0};
+
+	sync();
+	/* Allow Ctrl-Alt-Del to reboot system. */
+	reboot(RB_ENABLE_CAD);
+
+	/* Send signals to every process _except_ pid 1 */
+	message(console, "Sending SIGHUP to all processes.\r\n");
+	kill(-1, SIGHUP);
+	sleep(2);
+	sync();
+	message(console, "Sending SIGKILL to all processes.\r\n");
+	kill(-1, SIGKILL);
+	sleep(1);
+	waitfor(run("/bin/umount", umount_args, console, 0));
+	sync();
+	bdflush(1, 0);
+	sync();
+	reboot(do_reboot ?RB_AUTOBOOT : RB_HALT_SYSTEM);
+	exit(0);
+}
+
+static void
+halt_signal(int sig)
+{
+	shutdown_system(0);
+}
+
+static void
+reboot_signal(int sig)
+{
+	shutdown_system(1);
+}
+
+static void
+exit_signal(int sig)
+{
+  
+  /* initrd doesn't work anyway */
+
+  shutdown_system(1);
+
+	/* This is used on the initial ramdisk */
+
+  /*	message(log, "Init exiting.");
+	exit(0);
+	*/
+}
+
+void
+configure_terminals( int serial_cons );
+
+extern int
+init_main(struct FileInfo * i, int argc, char * * argv)
+{
+	static const char * const	rc = "etc/rc";
+	const char *				arguments[100];
+	int							run_rc = 1;
+	int							j;
+	int							pid1 = 0;
+	int							pid2 = 0;
+	int							create_swap= -1;
+	struct stat					statbuf;
+#ifndef INCLUDE_DINSTALL
+	const char *				tty_commands[2] = { "bin/sh", "bin/sh"};
+#else
+	const char *				tty_commands[2] = { "sbin/dinstall", "bin/sh"};
+#endif
+	char						swap[20];
+	int							serial_console = 0;
+
+	/*
+	 * If I am started as /linuxrc instead of /sbin/init, I don't have the
+	 * environment that init expects. I can't fix the signal behavior. Try
+	 * to divorce from the controlling terminal with setsid(). This won't work
+	 * if I am the process group leader.
+	 */
+	setsid();
+
+	signal(SIGUSR1, halt_signal);
+	signal(SIGUSR2, reboot_signal);
+	signal(SIGINT, reboot_signal);
+	signal(SIGTERM, exit_signal);
+
+	reboot(RB_DISABLE_CAD);
+
+	message(log, "%s: started. ", argv[0]);
+
+	for ( j = 1; j < argc; j++ ) {
+		if ( strcmp(argv[j], "single") == 0 ) {
+			run_rc = 0;
+			tty_commands[0] = "bin/sh";
+			tty_commands[1] = 0;
+		}
+	}
+	for ( j = 0; __environ[j] != 0; j++ ) {
+		if ( strncmp(__environ[j], "tty", 3) == 0
+		 && __environ[j][3] >= '1'
+		 && __environ[j][3] <= '2'
+		 && __environ[j][4] == '=' ) {
+			const char * s = &__environ[j][5];
+
+			if ( *s == 0 || strcmp(s, "off") == 0 )
+				s = 0;
+
+			tty_commands[__environ[j][3] - '1'] = s;
+		}
+		/* Should catch the syntax of Sparc kernel console setting.   */
+		/* The kernel does not recognize a serial console when getting*/
+		/* console=/dev/ttySX !! */
+		else if ( strcmp(__environ[j], "console=ttya") == 0 ) {
+			serial_console=1;
+		}
+		else if ( strcmp(__environ[j], "console=ttyb") == 0 ) {
+			serial_console=2;
+		}
+		/* standard console settings */
+		else if ( strncmp(__environ[j], "console=", 8) == 0 ) {
+			first_terminal=&(__environ[j][8]);
+		}
+		else if ( strncmp(__environ[j], "TERM=", 5) == 0) {
+			term_ptr=__environ[j];
+		}
+	}
+	configure_terminals( serial_console );
+
+	printf("mounting /proc ...\n");
+	if (mount("/proc","/proc","proc",0,0)) {
+	  perror("mounting /proc failed\n");
+	}
+	printf("\tdone.\n");
+
+	set_free_pages();
+
+	if (mem_total() < 3500) { /* not enough memory for standard install */
+	  int retval;
+	  retval= stat("/etc/swappartition",&statbuf);
+	  if (retval) {
+	    printf("
+You do not have enough RAM, hence you must boot using the Boot Disk
+for Low Memory systems.
+
+Read the instructions in the install.html file.
+");
+	    while (1) {;}
+	  } else { /* everything OK */
+	    FILE *f;
+
+	    f=fopen("/etc/swappartition","r");
+	    fgets(swap,19,f);
+	    fclose(f);
+	    *(strstr(swap,"\n"))='\0';
+
+	    if (swapon(swap,0)) {
+	      perror("swapon failed\n");
+	    } else {
+	      f=fopen("/etc/swaps","w");
+	      fprintf(f,"%s none swap rw 0 0",swap);
+		fclose(f);
+	      create_swap = 0;
+	    }
+	  }
+	}
+
+	/*
+	 * Don't modify **argv directly, it would show up in the "ps" display.
+	 * I don't want "init" to look like "rc".
+	 */
+	arguments[0] = rc;
+	for ( j = 1; j < argc; j++ ) {
+		arguments[j] = argv[j];
+	}
+	arguments[j] = 0;
+
+	if ( run_rc )
+		waitfor(run(rc, arguments, console, 0));
+
+	if ( 0 == create_swap) {
+	  if (unlink("/etc/swappartition")) {
+	    perror("unlinking /etc/swappartition");
+	  }
+	}
+
+	arguments[0] = "-sh";
+	arguments[1] = 0;
+	for ( ; ; ) {
+		int	wpid;
+		int	status;
+
+		if ( pid1 == 0 && tty_commands[0] ) {
+			/* Ask before starting a shell */
+			/*
+			 arguments[0] = tty_commands[0];
+			 */
+			pid1 = run(tty_commands[0], arguments, first_terminal, 0);
+		}
+		if ( pid2 == 0 && tty_commands[1] )
+			pid2 = run(tty_commands[1], arguments, second_terminal, 1);
+		wpid = wait(&status);
+		if ( wpid > 0 ) {
+			/* DEBUGGING */
+			message(log, "pid %d exited, status=%x.\n", wpid, status);
+		}
+		if ( wpid == pid1 ) {
+		  pid1 = 0;
+		}
+		if ( wpid == pid2 )
+			pid2 = 0;
+	}
+}
+
+void
+configure_terminals( int serial_cons )
+{
+	//struct stat statbuf;
+	char *tty;
+
+	switch (serial_cons) {
+	case 1:
+		strcpy( console, "/dev/ttyS0" );
+		break;
+	case 2:
+		strcpy( console, "/dev/ttyS1" );
+		break;
+	default:
+		tty = ttyname(0);
+		if (tty) {
+			strcpy( console, tty );
+			if (!strncmp( tty, "/dev/ttyS", 9 ))
+				serial_cons=1;
+		}
+		else
+			/* falls back to /dev/tty1 if an error occurs */
+			strcpy( console, default_console );
+	}
+	if (!first_terminal)
+		first_terminal = console;
+	if (serial_cons && !strncmp(term_ptr,"TERM=linux",10))
+		term_ptr = "TERM=vt100";
+}