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