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