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