init.c revision 1.2
1/* $NetBSD: init.c,v 1.22 1996/05/15 23:29:33 jtc Exp $ */ 2 3/*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Donn Seeley at Berkeley Software Design, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39#ifndef lint 40static char copyright[] = 41"@(#) Copyright (c) 1991, 1993\n\ 42 The Regents of the University of California. All rights reserved.\n"; 43#endif /* not lint */ 44 45#ifndef lint 46#if 0 47static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; 48#else 49static char rcsid[] = "$NetBSD: init.c,v 1.22 1996/05/15 23:29:33 jtc Exp $"; 50#endif 51#endif /* not lint */ 52 53#include <sys/param.h> 54#include <sys/sysctl.h> 55#include <sys/wait.h> 56 57#include <db.h> 58#include <errno.h> 59#include <fcntl.h> 60#include <signal.h> 61#include <stdio.h> 62#include <stdlib.h> 63#include <string.h> 64#include <syslog.h> 65#include <time.h> 66#include <ttyent.h> 67#include <unistd.h> 68#include <util.h> 69 70#ifdef __STDC__ 71#include <stdarg.h> 72#else 73#include <varargs.h> 74#endif 75 76#ifdef SECURE 77#include <pwd.h> 78#endif 79 80#include "pathnames.h" 81 82/* 83 * Sleep times; used to prevent thrashing. 84 */ 85#define GETTY_SPACING 5 /* N secs minimum getty spacing */ 86#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ 87#define WINDOW_WAIT 3 /* wait N secs after starting window */ 88#define STALL_TIMEOUT 30 /* wait N secs after warning */ 89#define DEATH_WATCH 10 /* wait N secs for procs to die */ 90 91void handle __P((sig_t, ...)); 92void delset __P((sigset_t *, ...)); 93 94void stall __P((char *, ...)); 95void warning __P((char *, ...)); 96void emergency __P((char *, ...)); 97void disaster __P((int)); 98void badsys __P((int)); 99 100/* 101 * We really need a recursive typedef... 102 * The following at least guarantees that the return type of (*state_t)() 103 * is sufficiently wide to hold a function pointer. 104 */ 105typedef long (*state_func_t) __P((void)); 106typedef state_func_t (*state_t) __P((void)); 107 108state_func_t single_user __P((void)); 109state_func_t runcom __P((void)); 110state_func_t read_ttys __P((void)); 111state_func_t multi_user __P((void)); 112state_func_t clean_ttys __P((void)); 113state_func_t catatonia __P((void)); 114state_func_t death __P((void)); 115 116enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 117 118void transition __P((state_t)); 119#ifndef LETS_GET_SMALL 120state_t requested_transition = runcom; 121#else /* LETS_GET_SMALL */ 122state_t requested_transition = single_user; 123#endif /* LETS_GET_SMALL */ 124 125void setctty __P((char *)); 126 127typedef struct init_session { 128 int se_index; /* index of entry in ttys file */ 129 pid_t se_process; /* controlling process */ 130 time_t se_started; /* used to avoid thrashing */ 131 int se_flags; /* status of session */ 132#define SE_SHUTDOWN 0x1 /* session won't be restarted */ 133#define SE_PRESENT 0x2 /* session is in /etc/ttys */ 134 char *se_device; /* filename of port */ 135 char *se_getty; /* what to run on that port */ 136 char **se_getty_argv; /* pre-parsed argument array */ 137 char *se_window; /* window system (started only once) */ 138 char **se_window_argv; /* pre-parsed argument array */ 139 struct init_session *se_prev; 140 struct init_session *se_next; 141} session_t; 142 143void free_session __P((session_t *)); 144session_t *new_session __P((session_t *, int, struct ttyent *)); 145session_t *sessions; 146 147char **construct_argv __P((char *)); 148void start_window_system __P((session_t *)); 149void collect_child __P((pid_t)); 150pid_t start_getty __P((session_t *)); 151void transition_handler __P((int)); 152void alrm_handler __P((int)); 153void setsecuritylevel __P((int)); 154int getsecuritylevel __P((void)); 155int setupargv __P((session_t *, struct ttyent *)); 156int clang; 157 158void clear_session_logs __P((session_t *)); 159 160int start_session_db __P((void)); 161void add_session __P((session_t *)); 162void del_session __P((session_t *)); 163session_t *find_session __P((pid_t)); 164DB *session_db; 165 166/* 167 * The mother of all processes. 168 */ 169int 170main(argc, argv) 171 int argc; 172 char **argv; 173{ 174 int c; 175 struct sigaction sa; 176 sigset_t mask; 177 178#ifndef LETS_GET_SMALL 179 /* Dispose of random users. */ 180 if (getuid() != 0) { 181 (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); 182 exit (1); 183 } 184 185 /* System V users like to reexec init. */ 186 if (getpid() != 1) { 187 (void)fprintf(stderr, "init: already running\n"); 188 exit (1); 189 } 190 191 /* 192 * Note that this does NOT open a file... 193 * Does 'init' deserve its own facility number? 194 */ 195 openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 196#endif /* LETS_GET_SMALL */ 197 198 /* 199 * Create an initial session. 200 */ 201 if (setsid() < 0) 202 warning("initial setsid() failed: %m"); 203 204 /* 205 * Establish an initial user so that programs running 206 * single user do not freak out and die (like passwd). 207 */ 208 if (setlogin("root") < 0) 209 warning("setlogin() failed: %m"); 210 211#ifndef LETS_GET_SMALL 212 /* 213 * This code assumes that we always get arguments through flags, 214 * never through bits set in some random machine register. 215 */ 216 while ((c = getopt(argc, argv, "sf")) != -1) 217 switch (c) { 218 case 's': 219 requested_transition = single_user; 220 break; 221 case 'f': 222 runcom_mode = FASTBOOT; 223 break; 224 default: 225 warning("unrecognized flag '-%c'", c); 226 break; 227 } 228 229 if (optind != argc) 230 warning("ignoring excess arguments"); 231#else /* LETS_GET_SMALL */ 232 requested_transition = single_user; 233#endif /* LETS_GET_SMALL */ 234 235 /* 236 * We catch or block signals rather than ignore them, 237 * so that they get reset on exec. 238 */ 239 handle(badsys, SIGSYS, 0); 240 handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 241 SIGBUS, SIGXCPU, SIGXFSZ, 0); 242 handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); 243 handle(alrm_handler, SIGALRM, 0); 244 sigfillset(&mask); 245 delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 246 SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); 247 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 248 sigemptyset(&sa.sa_mask); 249 sa.sa_flags = 0; 250 sa.sa_handler = SIG_IGN; 251 (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); 252 (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); 253 254 /* 255 * Paranoia. 256 */ 257 close(0); 258 close(1); 259 close(2); 260 261 /* 262 * Start the state machine. 263 */ 264 transition(requested_transition); 265 266 /* 267 * Should never reach here. 268 */ 269 return 1; 270} 271 272/* 273 * Associate a function with a signal handler. 274 */ 275void 276#ifdef __STDC__ 277handle(sig_t handler, ...) 278#else 279handle(va_alist) 280 va_dcl 281#endif 282{ 283 int sig; 284 struct sigaction sa; 285 int mask_everything; 286 va_list ap; 287#ifndef __STDC__ 288 sig_t handler; 289 290 va_start(ap); 291 handler = va_arg(ap, sig_t); 292#else 293 va_start(ap, handler); 294#endif 295 296 sa.sa_handler = handler; 297 sigfillset(&mask_everything); 298 299 while (sig = va_arg(ap, int)) { 300 sa.sa_mask = mask_everything; 301 /* XXX SA_RESTART? */ 302 sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 303 sigaction(sig, &sa, (struct sigaction *) 0); 304 } 305 va_end(ap); 306} 307 308/* 309 * Delete a set of signals from a mask. 310 */ 311void 312#ifdef __STDC__ 313delset(sigset_t *maskp, ...) 314#else 315delset(va_alist) 316 va_dcl 317#endif 318{ 319 int sig; 320 va_list ap; 321#ifndef __STDC__ 322 sigset_t *maskp; 323 324 va_start(ap); 325 maskp = va_arg(ap, sigset_t *); 326#else 327 va_start(ap, maskp); 328#endif 329 330 while (sig = va_arg(ap, int)) 331 sigdelset(maskp, sig); 332 va_end(ap); 333} 334 335/* 336 * Log a message and sleep for a while (to give someone an opportunity 337 * to read it and to save log or hardcopy output if the problem is chronic). 338 * NB: should send a message to the session logger to avoid blocking. 339 */ 340void 341#ifdef __STDC__ 342stall(char *message, ...) 343#else 344stall(va_alist) 345 va_dcl 346#endif 347{ 348 va_list ap; 349#ifndef __STDC__ 350 char *message; 351 352 va_start(ap); 353 message = va_arg(ap, char *); 354#else 355 va_start(ap, message); 356#endif 357 358 vsyslog(LOG_ALERT, message, ap); 359 va_end(ap); 360 sleep(STALL_TIMEOUT); 361} 362 363/* 364 * Like stall(), but doesn't sleep. 365 * If cpp had variadic macros, the two functions could be #defines for another. 366 * NB: should send a message to the session logger to avoid blocking. 367 */ 368void 369#ifdef __STDC__ 370warning(char *message, ...) 371#else 372warning(va_alist) 373 va_dcl 374#endif 375{ 376 va_list ap; 377#ifndef __STDC__ 378 char *message; 379 380 va_start(ap); 381 message = va_arg(ap, char *); 382#else 383 va_start(ap, message); 384#endif 385 386 vsyslog(LOG_ALERT, message, ap); 387 va_end(ap); 388} 389 390/* 391 * Log an emergency message. 392 * NB: should send a message to the session logger to avoid blocking. 393 */ 394void 395#ifdef __STDC__ 396emergency(char *message, ...) 397#else 398emergency(va_alist) 399 va_dcl 400#endif 401{ 402 va_list ap; 403#ifndef __STDC__ 404 char *message; 405 406 va_start(ap); 407 message = va_arg(ap, char *); 408#else 409 va_start(ap, message); 410#endif 411 412 vsyslog(LOG_EMERG, message, ap); 413 va_end(ap); 414} 415 416/* 417 * Catch a SIGSYS signal. 418 * 419 * These may arise if a system does not support sysctl. 420 * We tolerate up to 25 of these, then throw in the towel. 421 */ 422void 423badsys(sig) 424 int sig; 425{ 426 static int badcount = 0; 427 428 if (badcount++ < 25) 429 return; 430 disaster(sig); 431} 432 433/* 434 * Catch an unexpected signal. 435 */ 436void 437disaster(sig) 438 int sig; 439{ 440 emergency("fatal signal: %s", strsignal(sig)); 441 442 sleep(STALL_TIMEOUT); 443 _exit(sig); /* reboot */ 444} 445 446/* 447 * Get the security level of the kernel. 448 */ 449int 450getsecuritylevel() 451{ 452#ifdef KERN_SECURELVL 453 int name[2], curlevel; 454 size_t len; 455 extern int errno; 456 457 name[0] = CTL_KERN; 458 name[1] = KERN_SECURELVL; 459 len = sizeof curlevel; 460 if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 461 emergency("cannot get kernel security level: %s", 462 strerror(errno)); 463 return (-1); 464 } 465 return (curlevel); 466#else 467 return (-1); 468#endif 469} 470 471/* 472 * Set the security level of the kernel. 473 */ 474void 475setsecuritylevel(newlevel) 476 int newlevel; 477{ 478#ifdef KERN_SECURELVL 479 int name[2], curlevel; 480 extern int errno; 481 482 curlevel = getsecuritylevel(); 483 if (newlevel == curlevel) 484 return; 485 name[0] = CTL_KERN; 486 name[1] = KERN_SECURELVL; 487 if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 488 emergency( 489 "cannot change kernel security level from %d to %d: %s", 490 curlevel, newlevel, strerror(errno)); 491 return; 492 } 493#ifdef SECURE 494 warning("kernel security level changed from %d to %d", 495 curlevel, newlevel); 496#endif 497#endif 498} 499 500/* 501 * Change states in the finite state machine. 502 * The initial state is passed as an argument. 503 */ 504void 505transition(s) 506 state_t s; 507{ 508 for (;;) 509 s = (state_t) (*s)(); 510} 511 512/* 513 * Close out the accounting files for a login session. 514 * NB: should send a message to the session logger to avoid blocking. 515 */ 516void 517clear_session_logs(sp) 518 session_t *sp; 519{ 520 char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 521 522 if (logout(line)) 523 logwtmp(line, "", ""); 524} 525 526/* 527 * Start a session and allocate a controlling terminal. 528 * Only called by children of init after forking. 529 */ 530void 531setctty(name) 532 char *name; 533{ 534 int fd; 535 536 (void) revoke(name); 537 sleep (2); /* leave DTR low */ 538 if ((fd = open(name, O_RDWR)) == -1) { 539 stall("can't open %s: %m", name); 540 _exit(1); 541 } 542 if (login_tty(fd) == -1) { 543 stall("can't get %s for controlling terminal: %m", name); 544 _exit(1); 545 } 546} 547 548/* 549 * Bring the system up single user. 550 */ 551state_func_t 552single_user() 553{ 554 pid_t pid, wpid; 555 int status; 556 sigset_t mask; 557 char *shell = _PATH_BSHELL; 558 char *argv[2]; 559#ifdef SECURE 560 struct ttyent *typ; 561 struct passwd *pp; 562 static const char banner[] = 563 "Enter root password, or ^D to go multi-user\n"; 564 char *clear, *password; 565#endif 566 567 /* 568 * If the kernel is in secure mode, downgrade it to insecure mode. 569 */ 570 if (getsecuritylevel() > 0) 571 setsecuritylevel(0); 572 573 if ((pid = fork()) == 0) { 574 /* 575 * Start the single user session. 576 */ 577 setctty(_PATH_CONSOLE); 578 579#ifdef SECURE 580 /* 581 * Check the root password. 582 * We don't care if the console is 'on' by default; 583 * it's the only tty that can be 'off' and 'secure'. 584 */ 585 typ = getttynam("console"); 586 pp = getpwnam("root"); 587 if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { 588 write(2, banner, sizeof banner - 1); 589 for (;;) { 590 clear = getpass("Password:"); 591 if (clear == 0 || *clear == '\0') 592 _exit(0); 593 password = crypt(clear, pp->pw_passwd); 594 memset(clear, 0, _PASSWORD_LEN); 595 if (strcmp(password, pp->pw_passwd) == 0) 596 break; 597 warning("single-user login failed\n"); 598 } 599 } 600 endttyent(); 601 endpwent(); 602#endif /* SECURE */ 603 604#ifdef DEBUGSHELL 605 { 606 char altshell[128], *cp = altshell; 607 int num; 608 609#define SHREQUEST \ 610 "Enter pathname of shell or RETURN for sh: " 611 (void)write(STDERR_FILENO, 612 SHREQUEST, sizeof(SHREQUEST) - 1); 613 while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 614 num != 0 && *cp != '\n' && cp < &altshell[127]) 615 cp++; 616 *cp = '\0'; 617 if (altshell[0] != '\0') 618 shell = altshell; 619 } 620#endif /* DEBUGSHELL */ 621 622 /* 623 * Unblock signals. 624 * We catch all the interesting ones, 625 * and those are reset to SIG_DFL on exec. 626 */ 627 sigemptyset(&mask); 628 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 629 630 /* 631 * Fire off a shell. 632 * If the default one doesn't work, try the Bourne shell. 633 */ 634 argv[0] = "-sh"; 635 argv[1] = 0; 636 setenv("PATH", _PATH_STDPATH, 1); 637 execv(shell, argv); 638 emergency("can't exec %s for single user: %m", shell); 639 execv(_PATH_BSHELL, argv); 640 emergency("can't exec %s for single user: %m", _PATH_BSHELL); 641 sleep(STALL_TIMEOUT); 642 _exit(1); 643 } 644 645 if (pid == -1) { 646 /* 647 * We are seriously hosed. Do our best. 648 */ 649 emergency("can't fork single-user shell, trying again"); 650 while (waitpid(-1, (int *) 0, WNOHANG) > 0) 651 continue; 652 return (state_func_t) single_user; 653 } 654 655 requested_transition = 0; 656 do { 657 if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 658 collect_child(wpid); 659 if (wpid == -1) { 660 if (errno == EINTR) 661 continue; 662 warning("wait for single-user shell failed: %m; restarting"); 663 return (state_func_t) single_user; 664 } 665 if (wpid == pid && WIFSTOPPED(status)) { 666 warning("init: shell stopped, restarting\n"); 667 kill(pid, SIGCONT); 668 wpid = -1; 669 } 670 } while (wpid != pid && !requested_transition); 671 672 if (requested_transition) 673 return (state_func_t) requested_transition; 674 675 if (!WIFEXITED(status)) { 676 if (WTERMSIG(status) == SIGKILL) { 677 /* 678 * reboot(8) killed shell? 679 */ 680 warning("single user shell terminated."); 681 sleep(STALL_TIMEOUT); 682 _exit(0); 683 } else { 684 warning("single user shell terminated, restarting"); 685 return (state_func_t) single_user; 686 } 687 } 688 689 runcom_mode = FASTBOOT; 690#ifndef LETS_GET_SMALL 691 return (state_func_t) runcom; 692#else /* LETS_GET_SMALL */ 693 return (state_func_t) single_user; 694#endif /* LETS_GET_SMALL */ 695} 696 697#ifndef LETS_GET_SMALL 698/* 699 * Run the system startup script. 700 */ 701state_func_t 702runcom() 703{ 704 pid_t pid, wpid; 705 int status; 706 char *argv[4]; 707 struct sigaction sa; 708 709 if ((pid = fork()) == 0) { 710 sigemptyset(&sa.sa_mask); 711 sa.sa_flags = 0; 712 sa.sa_handler = SIG_IGN; 713 (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); 714 (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); 715 716 setctty(_PATH_CONSOLE); 717 718 argv[0] = "sh"; 719 argv[1] = _PATH_RUNCOM; 720 argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; 721 argv[3] = 0; 722 723 sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 724 725 execv(_PATH_BSHELL, argv); 726 stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 727 _exit(1); /* force single user mode */ 728 } 729 730 if (pid == -1) { 731 emergency("can't fork for %s on %s: %m", 732 _PATH_BSHELL, _PATH_RUNCOM); 733 while (waitpid(-1, (int *) 0, WNOHANG) > 0) 734 continue; 735 sleep(STALL_TIMEOUT); 736 return (state_func_t) single_user; 737 } 738 739 /* 740 * Copied from single_user(). This is a bit paranoid. 741 */ 742 do { 743 if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 744 collect_child(wpid); 745 if (wpid == -1) { 746 if (errno == EINTR) 747 continue; 748 warning("wait for %s on %s failed: %m; going to single user mode", 749 _PATH_BSHELL, _PATH_RUNCOM); 750 return (state_func_t) single_user; 751 } 752 if (wpid == pid && WIFSTOPPED(status)) { 753 warning("init: %s on %s stopped, restarting\n", 754 _PATH_BSHELL, _PATH_RUNCOM); 755 kill(pid, SIGCONT); 756 wpid = -1; 757 } 758 } while (wpid != pid); 759 760 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 761 requested_transition == catatonia) { 762 /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 763 sigset_t s; 764 765 sigfillset(&s); 766 for (;;) 767 sigsuspend(&s); 768 } 769 770 if (!WIFEXITED(status)) { 771 warning("%s on %s terminated abnormally, going to single user mode", 772 _PATH_BSHELL, _PATH_RUNCOM); 773 return (state_func_t) single_user; 774 } 775 776 if (WEXITSTATUS(status)) 777 return (state_func_t) single_user; 778 779 runcom_mode = AUTOBOOT; /* the default */ 780 /* NB: should send a message to the session logger to avoid blocking. */ 781 logwtmp("~", "reboot", ""); 782 return (state_func_t) read_ttys; 783} 784 785/* 786 * Open the session database. 787 * 788 * NB: We could pass in the size here; is it necessary? 789 */ 790int 791start_session_db() 792{ 793 if (session_db && (*session_db->close)(session_db)) 794 emergency("session database close: %s", strerror(errno)); 795 if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 796 emergency("session database open: %s", strerror(errno)); 797 return (1); 798 } 799 return (0); 800 801} 802 803/* 804 * Add a new login session. 805 */ 806void 807add_session(sp) 808 session_t *sp; 809{ 810 DBT key; 811 DBT data; 812 813 key.data = &sp->se_process; 814 key.size = sizeof sp->se_process; 815 data.data = &sp; 816 data.size = sizeof sp; 817 818 if ((*session_db->put)(session_db, &key, &data, 0)) 819 emergency("insert %d: %s", sp->se_process, strerror(errno)); 820} 821 822/* 823 * Delete an old login session. 824 */ 825void 826del_session(sp) 827 session_t *sp; 828{ 829 DBT key; 830 831 key.data = &sp->se_process; 832 key.size = sizeof sp->se_process; 833 834 if ((*session_db->del)(session_db, &key, 0)) 835 emergency("delete %d: %s", sp->se_process, strerror(errno)); 836} 837 838/* 839 * Look up a login session by pid. 840 */ 841session_t * 842#ifdef __STDC__ 843find_session(pid_t pid) 844#else 845find_session(pid) 846 pid_t pid; 847#endif 848{ 849 DBT key; 850 DBT data; 851 session_t *ret; 852 853 key.data = &pid; 854 key.size = sizeof pid; 855 if ((*session_db->get)(session_db, &key, &data, 0) != 0) 856 return 0; 857 memcpy(&ret, data.data, sizeof(ret)); 858 return ret; 859} 860 861/* 862 * Construct an argument vector from a command line. 863 */ 864char ** 865construct_argv(command) 866 char *command; 867{ 868 register int argc = 0; 869 register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 870 * sizeof (char *)); 871 static const char separators[] = " \t"; 872 873 if ((argv[argc++] = strtok(command, separators)) == 0) 874 return 0; 875 while (argv[argc++] = strtok((char *) 0, separators)) 876 continue; 877 return argv; 878} 879 880/* 881 * Deallocate a session descriptor. 882 */ 883void 884free_session(sp) 885 register session_t *sp; 886{ 887 free(sp->se_device); 888 if (sp->se_getty) { 889 free(sp->se_getty); 890 free(sp->se_getty_argv); 891 } 892 if (sp->se_window) { 893 free(sp->se_window); 894 free(sp->se_window_argv); 895 } 896 free(sp); 897} 898 899/* 900 * Allocate a new session descriptor. 901 */ 902session_t * 903new_session(sprev, session_index, typ) 904 session_t *sprev; 905 int session_index; 906 register struct ttyent *typ; 907{ 908 register session_t *sp; 909 910 if ((typ->ty_status & TTY_ON) == 0 || 911 typ->ty_name == 0 || 912 typ->ty_getty == 0) 913 return 0; 914 915 sp = (session_t *) malloc(sizeof (session_t)); 916 memset(sp, 0, sizeof *sp); 917 918 sp->se_flags = SE_PRESENT; 919 sp->se_index = session_index; 920 921 sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 922 (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 923 924 if (setupargv(sp, typ) == 0) { 925 free_session(sp); 926 return (0); 927 } 928 929 sp->se_next = 0; 930 if (sprev == 0) { 931 sessions = sp; 932 sp->se_prev = 0; 933 } else { 934 sprev->se_next = sp; 935 sp->se_prev = sprev; 936 } 937 938 return sp; 939} 940 941/* 942 * Calculate getty and if useful window argv vectors. 943 */ 944int 945setupargv(sp, typ) 946 session_t *sp; 947 struct ttyent *typ; 948{ 949 950 if (sp->se_getty) { 951 free(sp->se_getty); 952 free(sp->se_getty_argv); 953 } 954 sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); 955 (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); 956 sp->se_getty_argv = construct_argv(sp->se_getty); 957 if (sp->se_getty_argv == 0) { 958 warning("can't parse getty for port %s", sp->se_device); 959 free(sp->se_getty); 960 sp->se_getty = 0; 961 return (0); 962 } 963 if (typ->ty_window) { 964 if (sp->se_window) 965 free(sp->se_window); 966 sp->se_window = strdup(typ->ty_window); 967 sp->se_window_argv = construct_argv(sp->se_window); 968 if (sp->se_window_argv == 0) { 969 warning("can't parse window for port %s", 970 sp->se_device); 971 free(sp->se_window); 972 sp->se_window = 0; 973 return (0); 974 } 975 } 976 return (1); 977} 978 979/* 980 * Walk the list of ttys and create sessions for each active line. 981 */ 982state_func_t 983read_ttys() 984{ 985 int session_index = 0; 986 register session_t *sp, *snext; 987 register struct ttyent *typ; 988 989 /* 990 * Destroy any previous session state. 991 * There shouldn't be any, but just in case... 992 */ 993 for (sp = sessions; sp; sp = snext) { 994 if (sp->se_process) 995 clear_session_logs(sp); 996 snext = sp->se_next; 997 free_session(sp); 998 } 999 sessions = 0; 1000 if (start_session_db()) 1001 return (state_func_t) single_user; 1002 1003 /* 1004 * Allocate a session entry for each active port. 1005 * Note that sp starts at 0. 1006 */ 1007 while (typ = getttyent()) 1008 if (snext = new_session(sp, ++session_index, typ)) 1009 sp = snext; 1010 1011 endttyent(); 1012 1013 return (state_func_t) multi_user; 1014} 1015 1016/* 1017 * Start a window system running. 1018 */ 1019void 1020start_window_system(sp) 1021 session_t *sp; 1022{ 1023 pid_t pid; 1024 sigset_t mask; 1025 1026 if ((pid = fork()) == -1) { 1027 emergency("can't fork for window system on port %s: %m", 1028 sp->se_device); 1029 /* hope that getty fails and we can try again */ 1030 return; 1031 } 1032 1033 if (pid) 1034 return; 1035 1036 sigemptyset(&mask); 1037 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 1038 1039 if (setsid() < 0) 1040 emergency("setsid failed (window) %m"); 1041 1042 execv(sp->se_window_argv[0], sp->se_window_argv); 1043 stall("can't exec window system '%s' for port %s: %m", 1044 sp->se_window_argv[0], sp->se_device); 1045 _exit(1); 1046} 1047 1048/* 1049 * Start a login session running. 1050 */ 1051pid_t 1052start_getty(sp) 1053 session_t *sp; 1054{ 1055 pid_t pid; 1056 sigset_t mask; 1057 time_t current_time = time((time_t *) 0); 1058 1059 /* 1060 * fork(), not vfork() -- we can't afford to block. 1061 */ 1062 if ((pid = fork()) == -1) { 1063 emergency("can't fork for getty on port %s: %m", sp->se_device); 1064 return -1; 1065 } 1066 1067 if (pid) 1068 return pid; 1069 1070 if (current_time > sp->se_started && 1071 current_time - sp->se_started < GETTY_SPACING) { 1072 warning("getty repeating too quickly on port %s, sleeping", 1073 sp->se_device); 1074 sleep((unsigned) GETTY_SLEEP); 1075 } 1076 1077 if (sp->se_window) { 1078 start_window_system(sp); 1079 sleep(WINDOW_WAIT); 1080 } 1081 1082 sigemptyset(&mask); 1083 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 1084 1085 execv(sp->se_getty_argv[0], sp->se_getty_argv); 1086 stall("can't exec getty '%s' for port %s: %m", 1087 sp->se_getty_argv[0], sp->se_device); 1088 _exit(1); 1089} 1090#endif /* LETS_GET_SMALL */ 1091 1092/* 1093 * Collect exit status for a child. 1094 * If an exiting login, start a new login running. 1095 */ 1096void 1097#ifdef __STDC__ 1098collect_child(pid_t pid) 1099#else 1100collect_child(pid) 1101 pid_t pid; 1102#endif 1103{ 1104#ifndef LETS_GET_SMALL 1105 register session_t *sp, *sprev, *snext; 1106 1107 if (! sessions) 1108 return; 1109 1110 if (! (sp = find_session(pid))) 1111 return; 1112 1113 clear_session_logs(sp); 1114 del_session(sp); 1115 sp->se_process = 0; 1116 1117 if (sp->se_flags & SE_SHUTDOWN) { 1118 if (sprev = sp->se_prev) 1119 sprev->se_next = sp->se_next; 1120 else 1121 sessions = sp->se_next; 1122 if (snext = sp->se_next) 1123 snext->se_prev = sp->se_prev; 1124 free_session(sp); 1125 return; 1126 } 1127 1128 if ((pid = start_getty(sp)) == -1) { 1129 /* serious trouble */ 1130 requested_transition = clean_ttys; 1131 return; 1132 } 1133 1134 sp->se_process = pid; 1135 sp->se_started = time((time_t *) 0); 1136 add_session(sp); 1137#endif /* LETS_GET_SMALL */ 1138} 1139 1140/* 1141 * Catch a signal and request a state transition. 1142 */ 1143void 1144transition_handler(sig) 1145 int sig; 1146{ 1147 1148 switch (sig) { 1149#ifndef LETS_GET_SMALL 1150 case SIGHUP: 1151 requested_transition = clean_ttys; 1152 break; 1153 case SIGTERM: 1154 requested_transition = death; 1155 break; 1156 case SIGTSTP: 1157 requested_transition = catatonia; 1158 break; 1159#endif /* LETS_GET_SMALL */ 1160 default: 1161 requested_transition = 0; 1162 break; 1163 } 1164} 1165 1166#ifndef LETS_GET_SMALL 1167/* 1168 * Take the system multiuser. 1169 */ 1170state_func_t 1171multi_user() 1172{ 1173 pid_t pid; 1174 register session_t *sp; 1175 1176 requested_transition = 0; 1177 1178 /* 1179 * If the administrator has not set the security level to -1 1180 * to indicate that the kernel should not run multiuser in secure 1181 * mode, and the run script has not set a higher level of security 1182 * than level 1, then put the kernel into secure mode. 1183 */ 1184 if (getsecuritylevel() == 0) 1185 setsecuritylevel(1); 1186 1187 for (sp = sessions; sp; sp = sp->se_next) { 1188 if (sp->se_process) 1189 continue; 1190 if ((pid = start_getty(sp)) == -1) { 1191 /* serious trouble */ 1192 requested_transition = clean_ttys; 1193 break; 1194 } 1195 sp->se_process = pid; 1196 sp->se_started = time((time_t *) 0); 1197 add_session(sp); 1198 } 1199 1200 while (!requested_transition) 1201 if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 1202 collect_child(pid); 1203 1204 return (state_func_t) requested_transition; 1205} 1206 1207/* 1208 * This is an n-squared algorithm. We hope it isn't run often... 1209 */ 1210state_func_t 1211clean_ttys() 1212{ 1213 register session_t *sp, *sprev; 1214 register struct ttyent *typ; 1215 register int session_index = 0; 1216 register int devlen; 1217 1218 for (sp = sessions; sp; sp = sp->se_next) 1219 sp->se_flags &= ~SE_PRESENT; 1220 1221 devlen = sizeof(_PATH_DEV) - 1; 1222 while (typ = getttyent()) { 1223 ++session_index; 1224 1225 for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 1226 if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 1227 break; 1228 1229 if (sp) { 1230 sp->se_flags |= SE_PRESENT; 1231 if (sp->se_index != session_index) { 1232 warning("port %s changed utmp index from %d to %d", 1233 sp->se_device, sp->se_index, 1234 session_index); 1235 sp->se_index = session_index; 1236 } 1237 if ((typ->ty_status & TTY_ON) == 0 || 1238 typ->ty_getty == 0) { 1239 sp->se_flags |= SE_SHUTDOWN; 1240 kill(sp->se_process, SIGHUP); 1241 continue; 1242 } 1243 sp->se_flags &= ~SE_SHUTDOWN; 1244 if (setupargv(sp, typ) == 0) { 1245 warning("can't parse getty for port %s", 1246 sp->se_device); 1247 sp->se_flags |= SE_SHUTDOWN; 1248 kill(sp->se_process, SIGHUP); 1249 } 1250 continue; 1251 } 1252 1253 new_session(sprev, session_index, typ); 1254 } 1255 1256 endttyent(); 1257 1258 for (sp = sessions; sp; sp = sp->se_next) 1259 if ((sp->se_flags & SE_PRESENT) == 0) { 1260 sp->se_flags |= SE_SHUTDOWN; 1261 kill(sp->se_process, SIGHUP); 1262 } 1263 1264 return (state_func_t) multi_user; 1265} 1266 1267/* 1268 * Block further logins. 1269 */ 1270state_func_t 1271catatonia() 1272{ 1273 register session_t *sp; 1274 1275 for (sp = sessions; sp; sp = sp->se_next) 1276 sp->se_flags |= SE_SHUTDOWN; 1277 1278 return (state_func_t) multi_user; 1279} 1280#endif /* LETS_GET_SMALL */ 1281 1282/* 1283 * Note SIGALRM. 1284 */ 1285void 1286alrm_handler(sig) 1287 int sig; 1288{ 1289 clang = 1; 1290} 1291 1292#ifndef LETS_GET_SMALL 1293/* 1294 * Bring the system down to single user. 1295 */ 1296state_func_t 1297death() 1298{ 1299 register session_t *sp; 1300 register int i; 1301 pid_t pid; 1302 static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 1303 1304 for (sp = sessions; sp; sp = sp->se_next) 1305 sp->se_flags |= SE_SHUTDOWN; 1306 1307 /* NB: should send a message to the session logger to avoid blocking. */ 1308 logwtmp("~", "shutdown", ""); 1309 1310 for (i = 0; i < 3; ++i) { 1311 if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 1312 return (state_func_t) single_user; 1313 1314 clang = 0; 1315 alarm(DEATH_WATCH); 1316 do 1317 if ((pid = waitpid(-1, (int *)0, 0)) != -1) 1318 collect_child(pid); 1319 while (clang == 0 && errno != ECHILD); 1320 1321 if (errno == ECHILD) 1322 return (state_func_t) single_user; 1323 } 1324 1325 warning("some processes would not die; ps axl advised"); 1326 1327 return (state_func_t) single_user; 1328} 1329#endif /* LETS_GET_SMALL */ 1330