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