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 }