[9] | 1 | /* This process is the father (mother) of all Minix user processes. When
|
---|
| 2 | * Minix comes up, this is process number 2, and has a pid of 1. It
|
---|
| 3 | * executes the /etc/rc shell file, and then reads the /etc/ttytab file to
|
---|
| 4 | * determine which terminals need a login process.
|
---|
| 5 | *
|
---|
| 6 | * If the files /usr/adm/wtmp and /etc/utmp exist and are writable, init
|
---|
| 7 | * (with help from login) will maintain login accounting. Sending a
|
---|
| 8 | * signal 1 (SIGHUP) to init will cause it to rescan /etc/ttytab and start
|
---|
| 9 | * up new shell processes if necessary. It will not, however, kill off
|
---|
| 10 | * login processes for lines that have been turned off; do this manually.
|
---|
| 11 | * Signal 15 (SIGTERM) makes init stop spawning new processes, this is
|
---|
| 12 | * used by shutdown and friends when they are about to close the system
|
---|
| 13 | * down.
|
---|
| 14 | */
|
---|
| 15 |
|
---|
| 16 | #include <minix/type.h>
|
---|
| 17 | #include <sys/types.h>
|
---|
| 18 | #include <sys/wait.h>
|
---|
| 19 | #include <sys/stat.h>
|
---|
| 20 | #include <sys/svrctl.h>
|
---|
| 21 | #include <ttyent.h>
|
---|
| 22 | #include <errno.h>
|
---|
| 23 | #include <fcntl.h>
|
---|
| 24 | #include <limits.h>
|
---|
| 25 | #include <signal.h>
|
---|
| 26 | #include <string.h>
|
---|
| 27 | #include <time.h>
|
---|
| 28 | #include <stdlib.h>
|
---|
| 29 | #include <unistd.h>
|
---|
| 30 | #include <utmp.h>
|
---|
| 31 |
|
---|
| 32 | /* Command to execute as a response to the three finger salute. */
|
---|
| 33 | char *REBOOT_CMD[] = { "shutdown", "now", "CTRL-ALT-DEL", NULL };
|
---|
| 34 |
|
---|
| 35 | /* Associated fake ttytab entry. */
|
---|
| 36 | struct ttyent TT_REBOOT = { "console", "-", REBOOT_CMD, NULL };
|
---|
| 37 |
|
---|
| 38 | char PATH_UTMP[] = "/etc/utmp"; /* current logins */
|
---|
| 39 | char PATH_WTMP[] = "/usr/adm/wtmp"; /* login/logout history */
|
---|
| 40 |
|
---|
| 41 | #define PIDSLOTS 32 /* first this many ttys can be on */
|
---|
| 42 |
|
---|
| 43 | struct slotent {
|
---|
| 44 | int errct; /* error count */
|
---|
| 45 | pid_t pid; /* pid of login process for this tty line */
|
---|
| 46 | };
|
---|
| 47 |
|
---|
| 48 | #define ERRCT_DISABLE 10 /* disable after this many errors */
|
---|
| 49 | #define NO_PID 0 /* pid value indicating no process */
|
---|
| 50 |
|
---|
| 51 | struct slotent slots[PIDSLOTS]; /* init table of ttys and pids */
|
---|
| 52 |
|
---|
| 53 | int gothup = 0; /* flag, showing signal 1 was received */
|
---|
| 54 | int gotabrt = 0; /* flag, showing signal 6 was received */
|
---|
| 55 | int spawn = 1; /* flag, spawn processes only when set */
|
---|
| 56 |
|
---|
| 57 | void tell(int fd, char *s);
|
---|
| 58 | void report(int fd, char *label);
|
---|
| 59 | void wtmp(int type, int linenr, char *line, pid_t pid);
|
---|
| 60 | void startup(int linenr, struct ttyent *ttyp);
|
---|
| 61 | int execute(char **cmd);
|
---|
| 62 | void onhup(int sig);
|
---|
| 63 | void onterm(int sig);
|
---|
| 64 | void onabrt(int sig);
|
---|
| 65 |
|
---|
| 66 | int main(void)
|
---|
| 67 | {
|
---|
| 68 | pid_t pid; /* pid of child process */
|
---|
| 69 | int fd; /* generally useful */
|
---|
| 70 | int linenr; /* loop variable */
|
---|
| 71 | int check; /* check if a new process must be spawned */
|
---|
| 72 | struct slotent *slotp; /* slots[] pointer */
|
---|
| 73 | struct ttyent *ttyp; /* ttytab entry */
|
---|
| 74 | struct sigaction sa;
|
---|
| 75 | struct stat stb;
|
---|
| 76 |
|
---|
| 77 | if (fstat(0, &stb) < 0) {
|
---|
| 78 | /* Open standard input, output & error. */
|
---|
| 79 | (void) open("/dev/null", O_RDONLY);
|
---|
| 80 | (void) open("/dev/log", O_WRONLY);
|
---|
| 81 | dup(1);
|
---|
| 82 | }
|
---|
| 83 |
|
---|
| 84 | sigemptyset(&sa.sa_mask);
|
---|
| 85 | sa.sa_flags = 0;
|
---|
| 86 |
|
---|
| 87 | /* Hangup: Reexamine /etc/ttytab for newly enabled terminal lines. */
|
---|
| 88 | sa.sa_handler = onhup;
|
---|
| 89 | sigaction(SIGHUP, &sa, NULL);
|
---|
| 90 |
|
---|
| 91 | /* Terminate: Stop spawning login processes, shutdown is near. */
|
---|
| 92 | sa.sa_handler = onterm;
|
---|
| 93 | sigaction(SIGTERM, &sa, NULL);
|
---|
| 94 |
|
---|
| 95 | /* Abort: Sent by the kernel on CTRL-ALT-DEL; shut the system down. */
|
---|
| 96 | sa.sa_handler = onabrt;
|
---|
| 97 | sigaction(SIGABRT, &sa, NULL);
|
---|
| 98 |
|
---|
| 99 | /* Execute the /etc/rc file. */
|
---|
| 100 | if ((pid = fork()) != 0) {
|
---|
| 101 | /* Parent just waits. */
|
---|
| 102 | while (wait(NULL) != pid) {
|
---|
| 103 | if (gotabrt) reboot(RBT_HALT);
|
---|
| 104 | }
|
---|
| 105 | } else {
|
---|
| 106 | #if ! SYS_GETKENV
|
---|
| 107 | struct sysgetenv sysgetenv;
|
---|
| 108 | #endif
|
---|
| 109 | char bootopts[16];
|
---|
| 110 | static char *rc_command[] = { "sh", "/etc/rc", NULL, NULL, NULL };
|
---|
| 111 | char **rcp = rc_command + 2;
|
---|
| 112 |
|
---|
| 113 | /* Get the boot options from the boot environment. */
|
---|
| 114 | sysgetenv.key = "bootopts";
|
---|
| 115 | sysgetenv.keylen = 8+1;
|
---|
| 116 | sysgetenv.val = bootopts;
|
---|
| 117 | sysgetenv.vallen = sizeof(bootopts);
|
---|
| 118 | if (svrctl(MMGETPARAM, &sysgetenv) == 0) *rcp++ = bootopts;
|
---|
| 119 | *rcp = "start";
|
---|
| 120 |
|
---|
| 121 | execute(rc_command);
|
---|
| 122 | report(2, "sh /etc/rc");
|
---|
| 123 | _exit(1); /* impossible, we hope */
|
---|
| 124 | }
|
---|
| 125 |
|
---|
| 126 | /* Clear /etc/utmp if it exists. */
|
---|
| 127 | if ((fd = open(PATH_UTMP, O_WRONLY | O_TRUNC)) >= 0) close(fd);
|
---|
| 128 |
|
---|
| 129 | /* Log system reboot. */
|
---|
| 130 | wtmp(BOOT_TIME, 0, NULL, 0);
|
---|
| 131 |
|
---|
| 132 | /* Main loop. If login processes have already been started up, wait for one
|
---|
| 133 | * to terminate, or for a HUP signal to arrive. Start up new login processes
|
---|
| 134 | * for all ttys which don't have them. Note that wait() also returns when
|
---|
| 135 | * somebody's orphan dies, in which case ignore it. If the TERM signal is
|
---|
| 136 | * sent then stop spawning processes, shutdown time is near.
|
---|
| 137 | */
|
---|
| 138 |
|
---|
| 139 | check = 1;
|
---|
| 140 | while (1) {
|
---|
| 141 | while ((pid = waitpid(-1, NULL, check ? WNOHANG : 0)) > 0) {
|
---|
| 142 | /* Search to see which line terminated. */
|
---|
| 143 | for (linenr = 0; linenr < PIDSLOTS; linenr++) {
|
---|
| 144 | slotp = &slots[linenr];
|
---|
| 145 | if (slotp->pid == pid) {
|
---|
| 146 | /* Record process exiting. */
|
---|
| 147 | wtmp(DEAD_PROCESS, linenr, NULL, pid);
|
---|
| 148 | slotp->pid = NO_PID;
|
---|
| 149 | check = 1;
|
---|
| 150 | }
|
---|
| 151 | }
|
---|
| 152 | }
|
---|
| 153 |
|
---|
| 154 | /* If a signal 1 (SIGHUP) is received, simply reset error counts. */
|
---|
| 155 | if (gothup) {
|
---|
| 156 | gothup = 0;
|
---|
| 157 | for (linenr = 0; linenr < PIDSLOTS; linenr++) {
|
---|
| 158 | slots[linenr].errct = 0;
|
---|
| 159 | }
|
---|
| 160 | check = 1;
|
---|
| 161 | }
|
---|
| 162 |
|
---|
| 163 | /* Shut down on signal 6 (SIGABRT). */
|
---|
| 164 | if (gotabrt) {
|
---|
| 165 | gotabrt = 0;
|
---|
| 166 | startup(0, &TT_REBOOT);
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 | if (spawn && check) {
|
---|
| 170 | /* See which lines need a login process started up. */
|
---|
| 171 | for (linenr = 0; linenr < PIDSLOTS; linenr++) {
|
---|
| 172 | slotp = &slots[linenr];
|
---|
| 173 | if ((ttyp = getttyent()) == NULL) break;
|
---|
| 174 |
|
---|
| 175 | if (ttyp->ty_getty != NULL
|
---|
| 176 | && ttyp->ty_getty[0] != NULL
|
---|
| 177 | && slotp->pid == NO_PID
|
---|
| 178 | && slotp->errct < ERRCT_DISABLE)
|
---|
| 179 | {
|
---|
| 180 | startup(linenr, ttyp);
|
---|
| 181 | }
|
---|
| 182 | }
|
---|
| 183 | endttyent();
|
---|
| 184 | }
|
---|
| 185 | check = 0;
|
---|
| 186 | }
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | void onhup(int sig)
|
---|
| 190 | {
|
---|
| 191 | gothup = 1;
|
---|
| 192 | spawn = 1;
|
---|
| 193 | }
|
---|
| 194 |
|
---|
| 195 | void onterm(int sig)
|
---|
| 196 | {
|
---|
| 197 | spawn = 0;
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 | void onabrt(int sig)
|
---|
| 201 | {
|
---|
| 202 | static int count;
|
---|
| 203 |
|
---|
| 204 | if (++count == 2) reboot(RBT_HALT);
|
---|
| 205 | gotabrt = 1;
|
---|
| 206 | }
|
---|
| 207 |
|
---|
| 208 | void startup(int linenr, struct ttyent *ttyp)
|
---|
| 209 | {
|
---|
| 210 | /* Fork off a process for the indicated line. */
|
---|
| 211 |
|
---|
| 212 | struct slotent *slotp; /* pointer to ttyslot */
|
---|
| 213 | pid_t pid; /* new pid */
|
---|
| 214 | int err[2]; /* error reporting pipe */
|
---|
| 215 | char line[32]; /* tty device name */
|
---|
| 216 | int status;
|
---|
| 217 |
|
---|
| 218 | slotp = &slots[linenr];
|
---|
| 219 |
|
---|
| 220 | /* Error channel for between fork and exec. */
|
---|
| 221 | if (pipe(err) < 0) err[0] = err[1] = -1;
|
---|
| 222 |
|
---|
| 223 | if ((pid = fork()) == -1 ) {
|
---|
| 224 | report(2, "fork()");
|
---|
| 225 | sleep(10);
|
---|
| 226 | return;
|
---|
| 227 | }
|
---|
| 228 |
|
---|
| 229 | if (pid == 0) {
|
---|
| 230 | /* Child */
|
---|
| 231 | close(err[0]);
|
---|
| 232 | fcntl(err[1], F_SETFD, fcntl(err[1], F_GETFD) | FD_CLOEXEC);
|
---|
| 233 |
|
---|
| 234 | /* A new session. */
|
---|
| 235 | setsid();
|
---|
| 236 |
|
---|
| 237 | /* Construct device name. */
|
---|
| 238 | strcpy(line, "/dev/");
|
---|
| 239 | strncat(line, ttyp->ty_name, sizeof(line) - 6);
|
---|
| 240 |
|
---|
| 241 | /* Open the line for standard input and output. */
|
---|
| 242 | close(0);
|
---|
| 243 | close(1);
|
---|
| 244 | if (open(line, O_RDWR) < 0 || dup(0) < 0) {
|
---|
| 245 | write(err[1], &errno, sizeof(errno));
|
---|
| 246 | _exit(1);
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 | if (ttyp->ty_init != NULL && ttyp->ty_init[0] != NULL) {
|
---|
| 250 | /* Execute a command to initialize the terminal line. */
|
---|
| 251 |
|
---|
| 252 | if ((pid = fork()) == -1) {
|
---|
| 253 | report(2, "fork()");
|
---|
| 254 | errno= 0;
|
---|
| 255 | write(err[1], &errno, sizeof(errno));
|
---|
| 256 | _exit(1);
|
---|
| 257 | }
|
---|
| 258 |
|
---|
| 259 | if (pid == 0) {
|
---|
| 260 | alarm(10);
|
---|
| 261 | execute(ttyp->ty_init);
|
---|
| 262 | report(2, ttyp->ty_init[0]);
|
---|
| 263 | _exit(1);
|
---|
| 264 | }
|
---|
| 265 |
|
---|
| 266 | while (waitpid(pid, &status, 0) != pid) {}
|
---|
| 267 | if (status != 0) {
|
---|
| 268 | tell(2, "init: ");
|
---|
| 269 | tell(2, ttyp->ty_name);
|
---|
| 270 | tell(2, ": ");
|
---|
| 271 | tell(2, ttyp->ty_init[0]);
|
---|
| 272 | tell(2, ": bad exit status\n");
|
---|
| 273 | errno = 0;
|
---|
| 274 | write(err[1], &errno, sizeof(errno));
|
---|
| 275 | _exit(1);
|
---|
| 276 | }
|
---|
| 277 | }
|
---|
| 278 |
|
---|
| 279 | /* Redirect standard error too. */
|
---|
| 280 | dup2(0, 2);
|
---|
| 281 |
|
---|
| 282 | /* Execute the getty process. */
|
---|
| 283 | execute(ttyp->ty_getty);
|
---|
| 284 |
|
---|
| 285 | /* Oops, disaster strikes. */
|
---|
| 286 | fcntl(2, F_SETFL, fcntl(2, F_GETFL) | O_NONBLOCK);
|
---|
| 287 | if (linenr != 0) report(2, ttyp->ty_getty[0]);
|
---|
| 288 | write(err[1], &errno, sizeof(errno));
|
---|
| 289 | _exit(1);
|
---|
| 290 | }
|
---|
| 291 |
|
---|
| 292 | /* Parent */
|
---|
| 293 | if (ttyp != &TT_REBOOT) slotp->pid = pid;
|
---|
| 294 |
|
---|
| 295 | close(err[1]);
|
---|
| 296 | if (read(err[0], &errno, sizeof(errno)) != 0) {
|
---|
| 297 | /* If an errno value goes down the error pipe: Problems. */
|
---|
| 298 |
|
---|
| 299 | switch (errno) {
|
---|
| 300 | case ENOENT:
|
---|
| 301 | case ENODEV:
|
---|
| 302 | case ENXIO:
|
---|
| 303 | /* Device nonexistent, no driver, or no minor device. */
|
---|
| 304 | slotp->errct = ERRCT_DISABLE;
|
---|
| 305 | close(err[0]);
|
---|
| 306 | return;
|
---|
| 307 | case 0:
|
---|
| 308 | /* Error already reported. */
|
---|
| 309 | break;
|
---|
| 310 | default:
|
---|
| 311 | /* Any other error on the line. */
|
---|
| 312 | report(2, ttyp->ty_name);
|
---|
| 313 | }
|
---|
| 314 | close(err[0]);
|
---|
| 315 |
|
---|
| 316 | if (++slotp->errct >= ERRCT_DISABLE) {
|
---|
| 317 | tell(2, "init: ");
|
---|
| 318 | tell(2, ttyp->ty_name);
|
---|
| 319 | tell(2, ": excessive errors, shutting down\n");
|
---|
| 320 | } else {
|
---|
| 321 | sleep(5);
|
---|
| 322 | }
|
---|
| 323 | return;
|
---|
| 324 | }
|
---|
| 325 | close(err[0]);
|
---|
| 326 |
|
---|
| 327 | if (ttyp != &TT_REBOOT) wtmp(LOGIN_PROCESS, linenr, ttyp->ty_name, pid);
|
---|
| 328 | slotp->errct = 0;
|
---|
| 329 | }
|
---|
| 330 |
|
---|
| 331 | int execute(char **cmd)
|
---|
| 332 | {
|
---|
| 333 | /* Execute a command with a path search along /sbin:/bin:/usr/sbin:/usr/bin.
|
---|
| 334 | */
|
---|
| 335 | static char *nullenv[] = { NULL };
|
---|
| 336 | char command[128];
|
---|
| 337 | char *path[] = { "/sbin", "/bin", "/usr/sbin", "/usr/bin" };
|
---|
| 338 | int i;
|
---|
| 339 |
|
---|
| 340 | if (cmd[0][0] == '/') {
|
---|
| 341 | /* A full path. */
|
---|
| 342 | return execve(cmd[0], cmd, nullenv);
|
---|
| 343 | }
|
---|
| 344 |
|
---|
| 345 | /* Path search. */
|
---|
| 346 | for (i = 0; i < 4; i++) {
|
---|
| 347 | if (strlen(path[i]) + 1 + strlen(cmd[0]) + 1 > sizeof(command)) {
|
---|
| 348 | errno= ENAMETOOLONG;
|
---|
| 349 | return -1;
|
---|
| 350 | }
|
---|
| 351 | strcpy(command, path[i]);
|
---|
| 352 | strcat(command, "/");
|
---|
| 353 | strcat(command, cmd[0]);
|
---|
| 354 | execve(command, cmd, nullenv);
|
---|
| 355 | if (errno != ENOENT) break;
|
---|
| 356 | }
|
---|
| 357 | return -1;
|
---|
| 358 | }
|
---|
| 359 |
|
---|
| 360 | void wtmp(type, linenr, line, pid)
|
---|
| 361 | int type; /* type of entry */
|
---|
| 362 | int linenr; /* line number in ttytab */
|
---|
| 363 | char *line; /* tty name (only good on login) */
|
---|
| 364 | pid_t pid; /* pid of process */
|
---|
| 365 | {
|
---|
| 366 | /* Log an event into the UTMP and WTMP files. */
|
---|
| 367 |
|
---|
| 368 | struct utmp utmp; /* UTMP/WTMP User Accounting */
|
---|
| 369 | int fd;
|
---|
| 370 |
|
---|
| 371 | /* Clear the utmp record. */
|
---|
| 372 | memset((void *) &utmp, 0, sizeof(utmp));
|
---|
| 373 |
|
---|
| 374 | /* Fill in utmp. */
|
---|
| 375 | switch (type) {
|
---|
| 376 | case BOOT_TIME:
|
---|
| 377 | /* Make a special reboot record. */
|
---|
| 378 | strcpy(utmp.ut_name, "reboot");
|
---|
| 379 | strcpy(utmp.ut_line, "~");
|
---|
| 380 | break;
|
---|
| 381 |
|
---|
| 382 | case LOGIN_PROCESS:
|
---|
| 383 | /* A new login, fill in line name. */
|
---|
| 384 | strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
|
---|
| 385 | break;
|
---|
| 386 |
|
---|
| 387 | case DEAD_PROCESS:
|
---|
| 388 | /* A logout. Use the current utmp entry, but make sure it is a
|
---|
| 389 | * user process exiting, and not getty or login giving up.
|
---|
| 390 | */
|
---|
| 391 | if ((fd = open(PATH_UTMP, O_RDONLY)) < 0) {
|
---|
| 392 | if (errno != ENOENT) report(2, PATH_UTMP);
|
---|
| 393 | return;
|
---|
| 394 | }
|
---|
| 395 | if (lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1
|
---|
| 396 | || read(fd, &utmp, sizeof(utmp)) == -1
|
---|
| 397 | ) {
|
---|
| 398 | report(2, PATH_UTMP);
|
---|
| 399 | close(fd);
|
---|
| 400 | return;
|
---|
| 401 | }
|
---|
| 402 | close(fd);
|
---|
| 403 | if (utmp.ut_type != USER_PROCESS) return;
|
---|
| 404 | strncpy(utmp.ut_name, "", sizeof(utmp.ut_name));
|
---|
| 405 | break;
|
---|
| 406 | }
|
---|
| 407 |
|
---|
| 408 | /* Finish new utmp entry. */
|
---|
| 409 | utmp.ut_pid = pid;
|
---|
| 410 | utmp.ut_type = type;
|
---|
| 411 | utmp.ut_time = time((time_t *) 0);
|
---|
| 412 |
|
---|
| 413 | switch (type) {
|
---|
| 414 | case LOGIN_PROCESS:
|
---|
| 415 | case DEAD_PROCESS:
|
---|
| 416 | /* Write new entry to utmp. */
|
---|
| 417 | if ((fd = open(PATH_UTMP, O_WRONLY)) < 0
|
---|
| 418 | || lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1
|
---|
| 419 | || write(fd, &utmp, sizeof(utmp)) == -1
|
---|
| 420 | ) {
|
---|
| 421 | if (errno != ENOENT) report(2, PATH_UTMP);
|
---|
| 422 | }
|
---|
| 423 | if (fd != -1) close(fd);
|
---|
| 424 | break;
|
---|
| 425 | }
|
---|
| 426 |
|
---|
| 427 | switch (type) {
|
---|
| 428 | case BOOT_TIME:
|
---|
| 429 | case DEAD_PROCESS:
|
---|
| 430 | /* Add new wtmp entry. */
|
---|
| 431 | if ((fd = open(PATH_WTMP, O_WRONLY | O_APPEND)) < 0
|
---|
| 432 | || write(fd, &utmp, sizeof(utmp)) == -1
|
---|
| 433 | ) {
|
---|
| 434 | if (errno != ENOENT) report(2, PATH_WTMP);
|
---|
| 435 | }
|
---|
| 436 | if (fd != -1) close(fd);
|
---|
| 437 | break;
|
---|
| 438 | }
|
---|
| 439 | }
|
---|
| 440 |
|
---|
| 441 | void tell(fd, s)
|
---|
| 442 | int fd;
|
---|
| 443 | char *s;
|
---|
| 444 | {
|
---|
| 445 | write(fd, s, strlen(s));
|
---|
| 446 | }
|
---|
| 447 |
|
---|
| 448 | void report(fd, label)
|
---|
| 449 | int fd;
|
---|
| 450 | char *label;
|
---|
| 451 | {
|
---|
| 452 | int err = errno;
|
---|
| 453 |
|
---|
| 454 | tell(fd, "init: ");
|
---|
| 455 | tell(fd, label);
|
---|
| 456 | tell(fd, ": ");
|
---|
| 457 | tell(fd, strerror(err));
|
---|
| 458 | tell(fd, "\n");
|
---|
| 459 | errno= err;
|
---|
| 460 | }
|
---|