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