rats

RAdix iniT Suite
git clone https://git.daat.foo/repos/rats.git
Log | Files | Refs | README | LICENSE

origo.c (3909B)


      1 /* origo: PID 1 - minimal init */
      2 
      3 #ifndef _GNU_SOURCE
      4 #define _GNU_SOURCE /* needed for usleep, kill, and sig*() */
      5 #endif
      6 
      7 #include "util.h"
      8 
      9 #include <sys/mount.h>
     10 #include <sys/reboot.h>
     11 #include <sys/stat.h>
     12 #include <sys/wait.h>
     13 #include <errno.h>
     14 #include <fcntl.h>
     15 #include <signal.h>
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 #include <unistd.h>
     19 
     20 static sigset_t set;
     21 static pid_t serva_pid;
     22 static char *const serva_argv[] = { "serva", NULL };
     23 
     24 static pid_t
     25 spawn(const char *path, char *const argv[])
     26 {
     27 	pid_t pid;
     28 
     29 	pid = fork();
     30 	if (pid < 0)
     31 		eprintf("fork:");
     32 	if (pid == 0) {
     33 		sigprocmask(SIG_UNBLOCK, &set, NULL);
     34 		setsid();
     35 		setpgid(0, 0);
     36 		execvp(path, argv);
     37 		eprintf("execvp %s:", path);
     38 	}
     39 	return pid;
     40 }
     41 
     42 /* run_script: execute a shell script and wait for it */
     43 static void
     44 run_script(const char *path)
     45 {
     46 	char *const argv[] = { "sh", (char *)path, NULL };
     47 	pid_t pid;
     48 
     49 	if (access(path, F_OK) < 0)
     50 		return;
     51 	pid = spawn("/bin/sh", argv);
     52 	for (;;) {
     53 		if (waitpid(pid, NULL, 0) >= 0)
     54 			break;
     55 		if (errno != EINTR)
     56 			break;
     57 	}
     58 }
     59 
     60 /* mount_fs: bare-minimum kernel vfs */
     61 static void
     62 mount_fs(void)
     63 {
     64 	if (mount("proc", "/proc", "proc", MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL) < 0)
     65 		weprintf("mount /proc:");
     66 	if (mount("sysfs", "/sys", "sysfs", MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL) < 0)
     67 		weprintf("mount /sys:");
     68 	if (mount("devtmpfs", "/dev", "devtmpfs", 0, NULL) < 0)
     69 		weprintf("mount /dev:");
     70 	mkdir("/dev/pts", 0755);
     71 	if (mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC | MS_RELATIME, NULL) < 0)
     72 		weprintf("mount /dev/pts:");
     73 	mkdir("/dev/shm", 0755);
     74 	if (mount("tmpfs", "/dev/shm", "tmpfs", MS_NODEV | MS_NOSUID | MS_NOEXEC, NULL) < 0)
     75 		weprintf("mount /dev/shm:");
     76 }
     77 
     78 /* reap: handle SIGCHLD, respawn serva if it died */
     79 static void
     80 reap(void)
     81 {
     82 	pid_t pid;
     83 
     84 	while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
     85 		if (pid == serva_pid)
     86 			serva_pid = spawn("serva", serva_argv);
     87 	}
     88 }
     89 
     90 /* do_shutdown: stop serva, run rc.shutdown, sync, reboot */
     91 static void
     92 do_shutdown(int how)
     93 {
     94 	int i;
     95 
     96 	if (serva_pid > 0) {
     97 		kill(serva_pid, SIGTERM);
     98 		for (i = 0; i < 50; i++) {
     99 			if (waitpid(serva_pid, NULL, WNOHANG) > 0)
    100 				break;
    101 			if (i == 40)
    102 				kill(serva_pid, SIGKILL);
    103 			usleep(100000);
    104 		}
    105 		serva_pid = 0;
    106 	}
    107 	run_script("/etc/rc.shutdown");
    108 
    109 	/* kill any remaining processes - exclude PID 1 */
    110 	kill(-1, SIGTERM);
    111 	usleep(500000);
    112 	kill(-1, SIGKILL);
    113 
    114 	/* remount / ro */
    115 	if (mount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL) < 0)
    116 		weprintf("remount / read-only:");
    117 
    118 	sync();
    119 	reboot(how);
    120 	eprintf("reboot failed:");
    121 }
    122 
    123 /* single_user: stop serva, run rc.single or /bin/sh, respawn serva */
    124 static void
    125 single_user(void)
    126 {
    127 	if (serva_pid > 0) {
    128 		kill(serva_pid, SIGTERM);
    129 		waitpid(serva_pid, NULL, 0);
    130 		serva_pid = 0;
    131 	}
    132 	kill(-1, SIGTERM);
    133 	usleep(500000);
    134 	kill(-1, SIGKILL);
    135 	reap();
    136 	if (access("/etc/rc.single", F_OK) == 0)
    137 		run_script("/etc/rc.single");
    138 	else
    139 		run_script("/bin/sh");
    140 	serva_pid = spawn("serva", serva_argv);
    141 }
    142 
    143 int
    144 main(void)
    145 {
    146 	int sig;
    147 	int fd;
    148 
    149 	if (getpid() != 1)
    150 		eprintf("must be run as PID 1");
    151 
    152 	/* reopen console */
    153 	fd = open("/dev/console", O_RDWR);
    154 	if (fd >= 0) {
    155 		dup2(fd, STDIN_FILENO);
    156 		dup2(fd, STDOUT_FILENO);
    157 		dup2(fd, STDERR_FILENO);
    158 		if (fd > STDERR_FILENO)
    159 			close(fd);
    160 	}
    161 
    162 	/* block all signals — we use sigwait */
    163 	sigfillset(&set);
    164 	sigprocmask(SIG_BLOCK, &set, NULL);
    165 
    166 	setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
    167 
    168 	mount_fs();
    169 
    170 	run_script("/etc/rc.boot");
    171 
    172 	/* start supervisor */
    173 	serva_pid = spawn("serva", serva_argv);
    174 
    175 	/* signal loop */
    176 	for (;;) {
    177 		if (sigwait(&set, &sig) < 0)
    178 			continue;
    179 		switch (sig) {
    180 		case SIGCHLD:
    181 			reap();
    182 			break;
    183 		case SIGINT:
    184 		case SIGUSR2:
    185 			do_shutdown(RB_AUTOBOOT);
    186 			break;
    187 		case SIGUSR1:
    188 			do_shutdown(RB_POWER_OFF);
    189 			break;
    190 		case SIGTERM:
    191 			single_user();
    192 			break;
    193 		}
    194 	}
    195 	return 0;
    196 }