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