/*
* init.c
*
* This is the install type init
*
* Erik Troan (
[email protected])
*
* 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.
*
*/
#if USE_MINILIBC
#include "minilibc.h"
#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/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>
#define syslog klogctl
#endif
#define KICK_FLOPPY 1
#define KICK_BOOTP 2
#define MS_REMOUNT 32
#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/usr/bin:"
"/mnt/sysimage/usr/bin:/mnt/sysimage/usr/sbin:/mnt/sysimage/sbin",
"LD_LIBRARY_PATH=/lib:/usr/lib:/usr/X11R6/lib:/mnt/usr/lib:"
"/mnt/sysimage/lib:/mnt/sysimage/usr/lib",
"HOME=/",
"TERM=linux",
"DEBUG=",
"TERMINFO=/etc/linux-terminfo",
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;
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");
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())) {
/* child */
execve("/usr/bin/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);
}
}
}
}
#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
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 unmountFilesystems(void) {
int fd, size;
char buf[65535]; /* this should be big enough */
char * chptr, * start;
struct {
char * name;
int len;
} filesystems[500], tmp;
int numFilesystems = 0;
int i, j;
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) {
while (*chptr != ' ') chptr++;
chptr++;
start = chptr;
while (*chptr != ' ') chptr++;
*chptr++ = '\0';
filesystems[numFilesystems].name = start;
filesystems[numFilesystems].len = strlen(start);
numFilesystems++;
while (*chptr != '\n') chptr++;
chptr++;
}
/* look ma, a *bubble* sort */
for (i = 0; i < (numFilesystems - 1); i++) {
for (j = i; j < numFilesystems; j++) {
if (filesystems[i].len < filesystems[j].len) {
tmp = filesystems[i];
filesystems[i] = filesystems[j];
filesystems[j] = tmp;
}
}
}
/* -1 because the last one will always be '/' */
for (i = 0; i < numFilesystems - 1; i++) {
printf("\t%s", filesystems[i].name);
/* don't need to unmount /tmp. it is busy anyway. */
if (!testing && strncmp(filesystems[i].name, "/tmp", 4)) {
if (umount(filesystems[i].name) < 0) {
/* FIXME printf(" failed: %s", strerror(errno));*/
printstr(" umount failed");
}
}
printf("\n");
}
}
int main(void) {
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;
#ifdef __alpha__
char * kernel;
#endif
char * argv[15];
char ** argvp = argv;
/* getpid() != 1 should work, by linuxrc tends to get a larger pid */
testing = (getpid() > 50);
if (!testing) {
/* turn off screen blanking */
printstr("\033[9;0]");
printstr("\033[8]");
}
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 (unix89 pty) filesystem... ");
if (!testing) {
if (mount("/dev/pts", "/dev/pts", "devpts", 0, NULL))
fatal_error(1);
}
printf("done\n");
if (isSerial) {
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");
fd = open("/dev/ttyS0", O_RDWR, 0);
if (fd < 0) {
printf("failed to open /dev/ttyS0");
fatal_error(1);
}
setupTerminal(fd);
close(fd);
} else {
fd = open("/dev/tty1", O_RDWR, 0);
if (fd < 0) {
printf("failed to open /dev/tty1");
fatal_error(1);
}
}
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
setsid();
if (ioctl(0, TIOCSCTTY, NULL)) {
printf("could not set new controlling tty");
}
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("/", "/", 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 (!testing && roRoot) {
printf("creating 300k of ramdisk space... ");
if (doMke2fs("/dev/ram", "300"))
fatal_error(0);
printf("done\n");
printf("mounting /tmp from ramdisk... ");
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. */
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";
printf("running %s\n", argv[0]);
execve(argv[0], argv, 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("-- recieved signal %d", WTERMSIG(waitStatus));
}
printf("\n");
} else {
doReboot = 1;
}
if (testing)
exit(0);
sync(); sync();
if (!testing) {
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("unmounting filesystems...\n");
unmountFilesystems();
if (doReboot) {
printf("rebooting system\n");
sleep(2);
#if USE_MINILIBC
reboot(0xfee1dead, 672274793, 0x1234567);
#else
reboot(RB_AUTOBOOT);
#endif
} else {
printf("you may safely reboot your system\n");
while (1);
}
exit(0);
return 0;
}