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.9 (Berkeley) 04/13/87"; 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 struct sigvec mvec = { merge, sigmask(SIGTERM), 0 }; 251 /* 252 * Multi-user. Listen for users leaving, SIGHUP's 253 * which indicate ttys has changed, and SIGTERM's which 254 * are used to shutdown the system. 255 */ 256 multiple() 257 { 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 if (pid == -1) 266 return; 267 omask = sigblock(sigmask(SIGHUP)); 268 for (ALL) { 269 /* must restart window system BEFORE emulator */ 270 if (p->wpid == pid || p->wpid == -1) 271 wstart(p); 272 if (p->pid == pid || p->pid == -1) { 273 /* disown the window system */ 274 if (p->wpid) 275 kill(p->wpid, SIGHUP); 276 rmut(p); 277 dfork(p); 278 } 279 } 280 sigsetmask(omask); 281 } 282 } 283 284 /* 285 * Merge current contents of ttys file 286 * into in-core table of configured tty lines. 287 * Entered as signal handler for SIGHUP. 288 */ 289 #define FOUND 1 290 #define CHANGE 2 291 #define WCHANGE 4 292 293 merge() 294 { 295 register struct tab *p; 296 register struct ttyent *t; 297 register struct tab *p1; 298 299 for (ALL) 300 p->xflag = 0; 301 setttyent(); 302 while (t = getttyent()) { 303 if ((t->ty_status & TTY_ON) == 0) 304 continue; 305 for (ALL) { 306 if (SCMPN(p->line, t->ty_name)) 307 continue; 308 p->xflag |= FOUND; 309 if (SCMPN(p->comn, t->ty_getty)) { 310 p->xflag |= CHANGE; 311 SCPYN(p->comn, t->ty_getty); 312 } 313 if (SCMPN(p->wcmd, t->ty_window ? t->ty_window : "")) { 314 p->xflag |= WCHANGE|CHANGE; 315 SCPYN(p->wcmd, t->ty_window); 316 } 317 goto contin1; 318 } 319 320 /* 321 * Make space for a new one 322 */ 323 p1 = (struct tab *)calloc(1, sizeof(*p1)); 324 if (!p1) { 325 syslog(LOG_ERR, "no space for '%s' !?!", t->ty_name); 326 goto contin1; 327 } 328 /* 329 * Put new terminal at the end of the linked list. 330 */ 331 if (itab) { 332 for (p = itab; p->next ; p = p->next) 333 ; 334 p->next = p1; 335 } else 336 itab = p1; 337 338 p = p1; 339 SCPYN(p->line, t->ty_name); 340 p->xflag |= FOUND|CHANGE; 341 SCPYN(p->comn, t->ty_getty); 342 if (t->ty_window && strcmp(t->ty_window, "") != 0) { 343 p->xflag |= WCHANGE; 344 SCPYN(p->wcmd, t->ty_window); 345 } 346 contin1: 347 ; 348 } 349 endttyent(); 350 p1 = (struct tab *)0; 351 for (ALL) { 352 if ((p->xflag&FOUND) == 0) { 353 term(p); 354 wterm(p); 355 if (p1) 356 p1->next = p->next; 357 else 358 itab = p->next; 359 free(p); 360 p = p1 ? p1 : itab; 361 } else { 362 /* window system should be started first */ 363 if (p->xflag&WCHANGE) { 364 wterm(p); 365 wstart(p); 366 } 367 if (p->xflag&CHANGE) { 368 term(p); 369 dfork(p); 370 } 371 } 372 p1 = p; 373 } 374 } 375 376 term(p) 377 register struct tab *p; 378 { 379 380 if (p->pid != 0) { 381 rmut(p); 382 kill(p->pid, SIGKILL); 383 } 384 p->pid = 0; 385 /* send SIGHUP to get rid of connections */ 386 if (p->wpid > 0) 387 kill(p->wpid, SIGHUP); 388 } 389 390 dfork(p) 391 struct tab *p; 392 { 393 register pid; 394 time_t t; 395 int dowait = 0; 396 397 time(&t); 398 p->gettycnt++; 399 if ((t - p->gettytime) >= 60) { 400 p->gettytime = t; 401 p->gettycnt = 1; 402 } else if (p->gettycnt >= 5) { 403 dowait = 1; 404 p->gettytime = t; 405 p->gettycnt = 1; 406 } 407 pid = fork(); 408 if (pid == 0) { 409 signal(SIGTERM, SIG_DFL); 410 signal(SIGHUP, SIG_IGN); 411 sigsetmask(0); /* since can be called from masked code */ 412 if (dowait) { 413 syslog(LOG_ERR, "'%s %s' failing, sleeping", p->comn, p->line); 414 closelog(); 415 sleep(30); 416 } 417 execit(p->comn, p->line); 418 exit(0); 419 } 420 p->pid = pid; 421 } 422 423 /* 424 * Remove utmp entry. 425 */ 426 rmut(p) 427 register struct tab *p; 428 { 429 register f; 430 int found = 0; 431 static unsigned utmpsize; 432 static struct utmp *utmp; 433 register struct utmp *u; 434 int nutmp; 435 struct stat statbf; 436 437 f = open(utmpf, O_RDWR); 438 if (f >= 0) { 439 fstat(f, &statbf); 440 if (utmpsize < statbf.st_size) { 441 utmpsize = statbf.st_size + 10 * sizeof(struct utmp); 442 if (utmp) 443 utmp = (struct utmp *)realloc(utmp, utmpsize); 444 else 445 utmp = (struct utmp *)malloc(utmpsize); 446 if (!utmp) 447 syslog(LOG_ERR, "utmp malloc failed"); 448 } 449 if (statbf.st_size && utmp) { 450 nutmp = read(f, utmp, statbf.st_size); 451 nutmp /= sizeof(struct utmp); 452 for (u = utmp ; u < &utmp[nutmp] ; u++) { 453 if (u->ut_name[0] == 0 || 454 SCMPN(u->ut_line, p->line)) 455 continue; 456 lseek(f, ((long)u)-((long)utmp), L_SET); 457 SCPYN(u->ut_name, ""); 458 SCPYN(u->ut_host, ""); 459 time(&u->ut_time); 460 write(f, (char *)u, sizeof(*u)); 461 found++; 462 } 463 } 464 close(f); 465 } 466 if (found) { 467 f = open(wtmpf, O_WRONLY|O_APPEND); 468 if (f >= 0) { 469 SCPYN(wtmp.ut_line, p->line); 470 SCPYN(wtmp.ut_name, ""); 471 SCPYN(wtmp.ut_host, ""); 472 time(&wtmp.ut_time); 473 write(f, (char *)&wtmp, sizeof(wtmp)); 474 close(f); 475 } 476 /* 477 * After a proper login force reset 478 * of error detection code in dfork. 479 */ 480 p->gettytime = 0; 481 p->windtime = 0; 482 } 483 } 484 485 reset() 486 { 487 488 longjmp(sjbuf, 1); 489 } 490 491 jmp_buf idlebuf; 492 493 idlehup() 494 { 495 496 longjmp(idlebuf, 1); 497 } 498 499 idle() 500 { 501 register struct tab *p; 502 register pid; 503 504 signal(SIGHUP, idlehup); 505 for (EVER) { 506 if (setjmp(idlebuf)) 507 return; 508 pid = wait((int *) 0); 509 if (pid == -1) { 510 sigpause(0); 511 continue; 512 } 513 for (ALL) { 514 /* if window system dies, mark it for restart */ 515 if (p->wpid == pid) 516 p->wpid = -1; 517 if (p->pid == pid) { 518 rmut(p); 519 p->pid = -1; 520 } 521 } 522 } 523 } 524 525 wterm(p) 526 register struct tab *p; 527 { 528 if (p->wpid != 0) { 529 kill(p->wpid, SIGKILL); 530 } 531 p->wpid = 0; 532 } 533 534 wstart(p) 535 register struct tab *p; 536 { 537 register pid; 538 time_t t; 539 int dowait = 0; 540 541 time(&t); 542 p->windcnt++; 543 if ((t - p->windtime) >= 60) { 544 p->windtime = t; 545 p->windcnt = 1; 546 } else if (p->windcnt >= 5) { 547 dowait = 1; 548 p->windtime = t; 549 p->windcnt = 1; 550 } 551 552 pid = fork(); 553 554 if (pid == 0) { 555 signal(SIGTERM, SIG_DFL); 556 signal(SIGHUP, SIG_IGN); 557 sigsetmask(0); /* since can be called from masked code */ 558 if (dowait) { 559 syslog(LOG_ERR, "'%s %s' failing, sleeping", p->wcmd, p->line); 560 closelog(); 561 sleep(30); 562 } 563 execit(p->wcmd, p->line); 564 exit(0); 565 } 566 p->wpid = pid; 567 } 568 569 #define NARGS 20 /* must be at least 4 */ 570 #define ARGLEN 512 /* total size for all the argument strings */ 571 572 execit(s, arg) 573 char *s; 574 char *arg; /* last argument on line */ 575 { 576 char *argv[NARGS], args[ARGLEN], *envp[1]; 577 register char *sp = s; 578 register char *ap = args; 579 register char c; 580 register int i; 581 582 /* 583 * First we have to set up the argument vector. 584 * "prog arg1 arg2" maps to exec("prog", "-", "arg1", "arg2"). 585 */ 586 for (i = 1; i < NARGS - 2; i++) { 587 argv[i] = ap; 588 for (EVER) { 589 if ((c = *sp++) == '\0' || ap >= &args[ARGLEN-1]) { 590 *ap = '\0'; 591 goto done; 592 } 593 if (c == ' ') { 594 *ap++ = '\0'; 595 while (*sp == ' ') 596 sp++; 597 if (*sp == '\0') 598 goto done; 599 break; 600 } 601 *ap++ = c; 602 } 603 } 604 done: 605 argv[0] = argv[1]; 606 argv[1] = "-"; 607 argv[i+1] = arg; 608 argv[i+2] = 0; 609 envp[0] = 0; 610 execve(argv[0], &argv[1], envp); 611 /* report failure of exec */ 612 syslog(LOG_ERR, "%s: %m", argv[0]); 613 closelog(); 614 sleep(10); /* prevent failures from eating machine */ 615 } 616