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