1 /* 2 * Copyright (c) 1980,1986 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 #ifndef lint 8 static char sccsid[] = "@(#)init.c 5.15 (Berkeley) 05/27/90"; 9 #endif not lint 10 11 #include <sys/types.h> 12 #include <sys/file.h> 13 #include <sys/signal.h> 14 #include <sys/reboot.h> 15 #include <sys/syslog.h> 16 #include <sys/stat.h> 17 #include <sys/ioctl.h> 18 #include <setjmp.h> 19 #include <utmp.h> 20 #include <errno.h> 21 #include <ttyent.h> 22 #include "pathnames.h" 23 24 #define CMDSIZ 200 /* max string length for getty or window command*/ 25 #define ALL p = itab; p ; p = p->next 26 #define EVER ;; 27 #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 28 #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 29 30 char shell[] = _PATH_BSHELL; 31 char minus[] = "-"; 32 char runc[] = _PATH_RC; 33 char ctty[] = _PATH_CONSOLE; 34 35 struct tab 36 { 37 char line[UT_LINESIZE]; 38 char comn[CMDSIZ]; 39 char xflag; 40 int pid; 41 int wpid; /* window system pid for SIGHUP */ 42 char wcmd[CMDSIZ]; /* command to start window system process */ 43 time_t gettytime; 44 int gettycnt; 45 time_t windtime; 46 int windcnt; 47 struct tab *next; 48 } *itab; 49 50 int fi; 51 int mergflag; 52 char tty[20]; 53 jmp_buf sjbuf, shutpass; 54 55 char *strcpy(), *strcat(); 56 long lseek(); 57 void idle(), merge(), reset(); 58 59 struct sigvec rvec = { reset, sigmask(SIGHUP), 0 }; 60 61 #if defined(vax) || defined(tahoe) 62 main() 63 { 64 #if defined(tahoe) 65 register int r12; /* make sure r11 gets bootflags */ 66 #endif 67 register int r11; /* passed thru from boot */ 68 #else 69 main(argc, argv) 70 char **argv; 71 { 72 #endif 73 int howto, oldhowto, started; 74 75 #if defined(vax) || defined(tahoe) 76 howto = r11; 77 #else 78 howto = 0; 79 if (argc > 1 && argv[1][0] == '-') { 80 char *cp; 81 82 cp = &argv[1][1]; 83 while (*cp) switch (*cp++) { 84 case 'a': 85 howto |= RB_ASKNAME; 86 break; 87 case 's': 88 howto |= RB_SINGLE; 89 break; 90 } 91 } else { 92 howto = RB_SINGLE; 93 } 94 #endif 95 if (getuid() != 0) 96 exit(1); 97 openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 98 if (setsid() < 0) 99 syslog(LOG_ERR, "setsid failed (initial) %m"); 100 sigvec(SIGTERM, &rvec, (struct sigvec *)0); 101 signal(SIGTSTP, idle); 102 signal(SIGSTOP, SIG_IGN); 103 signal(SIGTTIN, SIG_IGN); 104 signal(SIGTTOU, SIG_IGN); 105 (void) setjmp(sjbuf); 106 for (started = 0; ; ) { 107 oldhowto = howto; 108 howto = RB_SINGLE; 109 if (started && setjmp(shutpass) == 0) 110 shutdown(); 111 started = 1; 112 if (oldhowto & RB_SINGLE) 113 single(); 114 if (runcom(oldhowto) == 0) 115 continue; 116 merge(); 117 multiple(); 118 } 119 } 120 121 void shutreset(); 122 123 shutdown() 124 { 125 register i; 126 register struct tab *p, *p1; 127 128 signal(SIGHUP, SIG_IGN); 129 for (p = itab; p ; ) { 130 term(p); 131 p1 = p->next; 132 free(p); 133 p = p1; 134 } 135 itab = (struct tab *)0; 136 signal(SIGALRM, shutreset); 137 (void) kill(-1, SIGTERM); /* one chance to catch it */ 138 sleep(5); 139 alarm(30); 140 for (i = 0; i < 5; i++) 141 kill(-1, SIGKILL); 142 while (wait((int *)0) != -1) 143 ; 144 alarm(0); 145 shutend(); 146 } 147 148 char shutfailm[] = "init: WARNING: something is hung (won't die); ps axl advised\n"; 149 150 void 151 shutreset() 152 { 153 int status; 154 155 if (fork() == 0) { 156 int ct = open(ctty, 1); 157 write(ct, shutfailm, sizeof (shutfailm)); 158 sleep(5); 159 exit(1); 160 } 161 sleep(5); 162 shutend(); 163 longjmp(shutpass, 1); 164 } 165 166 shutend() 167 { 168 register i, f; 169 170 acct(0); 171 signal(SIGALRM, SIG_DFL); 172 for (i = 0; i < 10; i++) 173 close(i); 174 logwtmp("~", "shutdown", ""); 175 } 176 177 single() 178 { 179 register pid; 180 register xpid; 181 extern int errno; 182 183 do { 184 pid = fork(); 185 if (pid == 0) { 186 signal(SIGTERM, SIG_DFL); 187 signal(SIGHUP, SIG_DFL); 188 signal(SIGALRM, SIG_DFL); 189 signal(SIGTSTP, SIG_IGN); 190 if (setsid() < 0) 191 syslog(LOG_ERR, "setsid failed (single): %m"); 192 (void) open(ctty, O_RDWR); 193 if (ioctl(0, TIOCSCTTY, 0) < 0) 194 syslog(LOG_ERR, "TIOCSCTTY failed: %m"); 195 dup2(0, 1); 196 dup2(0, 2); 197 execl(shell, minus, (char *)0); 198 perror(shell); 199 exit(0); 200 } 201 while ((xpid = wait((int *)0)) != pid) 202 if (xpid == -1 && errno == ECHILD) 203 break; 204 } while (xpid == -1); 205 } 206 207 runcom(oldhowto) 208 int oldhowto; 209 { 210 register pid, f; 211 int status; 212 213 pid = fork(); 214 if (pid == 0) { 215 (void) open(ctty, O_RDONLY); 216 dup2(0, 1); 217 dup2(0, 2); 218 if (setsid() < 0) 219 syslog(LOG_ERR, "setsid failed (runcom) %m"); 220 if (ioctl(0, TIOCSCTTY, 0) < 0) 221 syslog(LOG_ERR, "TIOCSCTTY failed (runcom) %m"); 222 if (oldhowto & RB_SINGLE) 223 execl(shell, shell, runc, (char *)0); 224 else 225 execl(shell, shell, runc, "autoboot", (char *)0); 226 exit(1); 227 } 228 while (wait(&status) != pid) 229 ; 230 if (status) 231 return (0); 232 logwtmp("~", "reboot", ""); 233 return (1); 234 } 235 236 struct sigvec mvec = { merge, sigmask(SIGTERM), 0 }; 237 /* 238 * Multi-user. Listen for users leaving, SIGHUP's 239 * which indicate ttys has changed, and SIGTERM's which 240 * are used to shutdown the system. 241 */ 242 multiple() 243 { 244 extern int errno; 245 register struct tab *p; 246 register pid; 247 int omask; 248 249 sigvec(SIGHUP, &mvec, (struct sigvec *)0); 250 for (EVER) { 251 pid = wait((int *)0); 252 /* SHOULD FIX THIS IN THE KERNEL */ 253 if (pid == -1 && errno != EINTR) 254 return; 255 omask = sigblock(sigmask(SIGHUP)); 256 for (ALL) { 257 /* must restart window system BEFORE emulator */ 258 if (p->wpid == pid || p->wpid == -1) 259 wstart(p); 260 if (p->pid == pid || p->pid == -1) { 261 /* disown the window system */ 262 if (p->wpid) 263 kill(p->wpid, SIGHUP); 264 cleanutmp(p); 265 dfork(p); 266 } 267 } 268 sigsetmask(omask); 269 } 270 } 271 272 /* 273 * Merge current contents of ttys file 274 * into in-core table of configured tty lines. 275 * Entered as signal handler for SIGHUP. 276 */ 277 #define FOUND 1 278 #define CHANGE 2 279 #define WCHANGE 4 280 281 void 282 merge() 283 { 284 register struct tab *p; 285 register struct ttyent *t; 286 register struct tab *p1; 287 288 for (ALL) 289 p->xflag = 0; 290 setttyent(); 291 while (t = getttyent()) { 292 if ((t->ty_status & TTY_ON) == 0) 293 continue; 294 for (ALL) { 295 if (SCMPN(p->line, t->ty_name)) 296 continue; 297 p->xflag |= FOUND; 298 if (SCMPN(p->comn, t->ty_getty)) { 299 p->xflag |= CHANGE; 300 SCPYN(p->comn, t->ty_getty); 301 } 302 if (SCMPN(p->wcmd, t->ty_window ? t->ty_window : "")) { 303 p->xflag |= WCHANGE|CHANGE; 304 SCPYN(p->wcmd, t->ty_window); 305 } 306 goto contin1; 307 } 308 309 /* 310 * Make space for a new one 311 */ 312 p1 = (struct tab *)calloc(1, sizeof(*p1)); 313 if (!p1) { 314 syslog(LOG_ERR, "no space for '%s' !?!", t->ty_name); 315 goto contin1; 316 } 317 /* 318 * Put new terminal at the end of the linked list. 319 */ 320 if (itab) { 321 for (p = itab; p->next ; p = p->next) 322 ; 323 p->next = p1; 324 } else 325 itab = p1; 326 327 p = p1; 328 SCPYN(p->line, t->ty_name); 329 p->xflag |= FOUND|CHANGE; 330 SCPYN(p->comn, t->ty_getty); 331 if (t->ty_window && strcmp(t->ty_window, "") != 0) { 332 p->xflag |= WCHANGE; 333 SCPYN(p->wcmd, t->ty_window); 334 } 335 contin1: 336 ; 337 } 338 endttyent(); 339 p1 = (struct tab *)0; 340 for (ALL) { 341 if ((p->xflag&FOUND) == 0) { 342 term(p); 343 wterm(p); 344 if (p1) 345 p1->next = p->next; 346 else 347 itab = p->next; 348 free(p); 349 p = p1 ? p1 : itab; 350 } else { 351 /* window system should be started first */ 352 if (p->xflag&WCHANGE) { 353 wterm(p); 354 wstart(p); 355 } 356 if (p->xflag&CHANGE) { 357 term(p); 358 dfork(p); 359 } 360 } 361 p1 = p; 362 } 363 } 364 365 term(p) 366 register struct tab *p; 367 { 368 369 if (p->pid != 0) { 370 cleanutmp(p); 371 kill(p->pid, SIGKILL); 372 } 373 p->pid = 0; 374 /* send SIGHUP to get rid of connections */ 375 if (p->wpid > 0) 376 kill(p->wpid, SIGHUP); 377 } 378 379 dfork(p) 380 struct tab *p; 381 { 382 register pid; 383 time_t t; 384 int dowait = 0; 385 386 time(&t); 387 p->gettycnt++; 388 if ((t - p->gettytime) >= 60) { 389 p->gettytime = t; 390 p->gettycnt = 1; 391 } else if (p->gettycnt >= 5) { 392 dowait = 1; 393 p->gettytime = t; 394 p->gettycnt = 1; 395 } 396 pid = fork(); 397 if (pid == 0) { 398 signal(SIGTERM, SIG_DFL); 399 signal(SIGHUP, SIG_IGN); 400 sigsetmask(0); /* since can be called from masked code */ 401 if (dowait) { 402 syslog(LOG_ERR, "'%s %s' failing, sleeping", p->comn, p->line); 403 closelog(); 404 sleep(30); 405 } 406 if (setsid() < 0) 407 syslog(LOG_ERR, "setsid failed(dfork) %m"); 408 execit(p->comn, p->line); 409 exit(0); 410 } 411 p->pid = pid; 412 } 413 414 cleanutmp(p) 415 register struct tab *p; 416 { 417 if (logout(p->line)) { 418 logwtmp(p->line, "", ""); 419 /* 420 * After a proper login force reset 421 * of error detection code in dfork. 422 */ 423 p->gettytime = 0; 424 p->windtime = 0; 425 } 426 } 427 428 void 429 reset() 430 { 431 longjmp(sjbuf, 1); 432 } 433 434 jmp_buf idlebuf; 435 436 void 437 idlehup() 438 { 439 longjmp(idlebuf, 1); 440 } 441 442 void 443 idle() 444 { 445 register struct tab *p; 446 register pid; 447 448 signal(SIGHUP, idlehup); 449 for (EVER) { 450 if (setjmp(idlebuf)) 451 return; 452 pid = wait((int *) 0); 453 if (pid == -1) { 454 sigpause(0); 455 continue; 456 } 457 for (ALL) { 458 /* if window system dies, mark it for restart */ 459 if (p->wpid == pid) 460 p->wpid = -1; 461 if (p->pid == pid) { 462 cleanutmp(p); 463 p->pid = -1; 464 } 465 } 466 } 467 } 468 469 wterm(p) 470 register struct tab *p; 471 { 472 if (p->wpid != 0) { 473 kill(p->wpid, SIGKILL); 474 } 475 p->wpid = 0; 476 } 477 478 wstart(p) 479 register struct tab *p; 480 { 481 register pid; 482 time_t t; 483 int dowait = 0; 484 485 time(&t); 486 p->windcnt++; 487 if ((t - p->windtime) >= 60) { 488 p->windtime = t; 489 p->windcnt = 1; 490 } else if (p->windcnt >= 5) { 491 dowait = 1; 492 p->windtime = t; 493 p->windcnt = 1; 494 } 495 496 pid = fork(); 497 498 if (pid == 0) { 499 signal(SIGTERM, SIG_DFL); 500 signal(SIGHUP, SIG_IGN); 501 sigsetmask(0); /* since can be called from masked code */ 502 if (dowait) { 503 syslog(LOG_ERR, "'%s %s' failing, sleeping", p->wcmd, p->line); 504 closelog(); 505 sleep(30); 506 } 507 if (setsid() < 0) 508 syslog(LOG_ERR, "setsid failed (window) %m"); 509 execit(p->wcmd, p->line); 510 exit(0); 511 } 512 p->wpid = pid; 513 } 514 515 #define NARGS 20 /* must be at least 4 */ 516 #define ARGLEN 512 /* total size for all the argument strings */ 517 518 execit(s, arg) 519 char *s; 520 char *arg; /* last argument on line */ 521 { 522 char *argv[NARGS], args[ARGLEN], *envp[1]; 523 register char *sp = s; 524 register char *ap = args; 525 register char c; 526 register int i; 527 528 /* 529 * First we have to set up the argument vector. 530 * "prog arg1 arg2" maps to exec("prog", "-", "arg1", "arg2"). 531 */ 532 for (i = 1; i < NARGS - 2; i++) { 533 argv[i] = ap; 534 for (EVER) { 535 if ((c = *sp++) == '\0' || ap >= &args[ARGLEN-1]) { 536 *ap = '\0'; 537 goto done; 538 } 539 if (c == ' ') { 540 *ap++ = '\0'; 541 while (*sp == ' ') 542 sp++; 543 if (*sp == '\0') 544 goto done; 545 break; 546 } 547 *ap++ = c; 548 } 549 } 550 done: 551 argv[0] = argv[1]; 552 argv[1] = "-"; 553 argv[i+1] = arg; 554 argv[i+2] = 0; 555 envp[0] = 0; 556 execve(argv[0], &argv[1], envp); 557 /* report failure of exec */ 558 syslog(LOG_ERR, "%s: %m", argv[0]); 559 closelog(); 560 sleep(10); /* prevent failures from eating machine */ 561 } 562