diff options
Diffstat (limited to 'init/init.c')
-rw-r--r-- | init/init.c | 438 |
1 files changed, 438 insertions, 0 deletions
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"; +} |