diff options
Diffstat (limited to 'sbin/init')
-rw-r--r-- | sbin/init/Makefile | 12 | ||||
-rw-r--r-- | sbin/init/NOTES | 114 | ||||
-rw-r--r-- | sbin/init/init.8 | 293 | ||||
-rw-r--r-- | sbin/init/init.c | 1335 | ||||
-rw-r--r-- | sbin/init/pathnames.h | 44 |
5 files changed, 1798 insertions, 0 deletions
diff --git a/sbin/init/Makefile b/sbin/init/Makefile new file mode 100644 index 00000000000..80edef1b614 --- /dev/null +++ b/sbin/init/Makefile @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.16 1995/03/18 14:56:28 cgd Exp $ +# @(#)Makefile 8.1 (Berkeley) 7/19/93 + +PROG= init +MAN= init.8 +DPADD= ${LIBUTIL} ${LIBCRYPT} +LDADD= -lutil -lcrypt +BINMODE=500 +INSTALLFLAGS=-fschg +CFLAGS+=-DDEBUGSHELL -DSECURE + +.include <bsd.prog.mk> diff --git a/sbin/init/NOTES b/sbin/init/NOTES new file mode 100644 index 00000000000..f6547d739dc --- /dev/null +++ b/sbin/init/NOTES @@ -0,0 +1,114 @@ +$NetBSD: NOTES,v 1.2 1995/03/18 14:56:29 cgd Exp $ + +POSIX and init: +-------------- + +POSIX.1 does not define 'init' but it mentions it in a few places. + +B.2.2.2, p205 line 873: + + This is part of the extensive 'job control' glossary entry. + This specific reference says that 'init' must by default provide + protection from job control signals to jobs it starts -- + it sets SIGTSTP, SIGTTIN and SIGTTOU to SIG_IGN. + +B.2.2.2, p206 line 889: + + Here is a reference to 'vhangup'. It says, 'POSIX.1 does + not specify how controlling terminal access is affected by + a user logging out (that is, by a controlling process + terminating).' vhangup() is recognized as one way to handle + the problem. I'm not clear what happens in Reno; I have + the impression that when the controlling process terminates, + references to the controlling terminal are converted to + references to a 'dead' vnode. I don't know whether vhangup() + is required. + +B.2.2.2, p206 line 921: + + Orphaned process groups bear indirectly on this issue. A + session leader's process group is considered to be orphaned; + that is, it's immune to job control signals from the terminal. + +B.2.2.2, p233 line 2055: + + 'Historically, the implementation-dependent process that + inherits children whose parents have terminated without + waiting on them is called "init" and has a process ID of 1.' + + It goes on to note that it used to be the case that 'init' + was responsible for sending SIGHUP to the foreground process + group of a tty whose controlling process has exited, using + vhangup(). It is now the responsibility of the kernel to + do this when the controlling process calls _exit(). The + kernel is also responsible for sending SIGCONT to stopped + process groups that become orphaned. This is like old BSD + but entire process groups are signaled instead of individual + processes. + + In general it appears that the kernel now automatically + takes care of orphans, relieving 'init' of any responsibility. + Specifics are listed on the _exit() page (p50). + +On setsid(): +----------- + +It appears that neither getty nor login call setsid(), so init must +do this -- seems reasonable. B.4.3.2 p 248 implies that this is the +way that 'init' should work; it says that setsid() should be called +after forking. + +Process group leaders cannot call setsid() -- another reason to +fork! Of course setsid() causes the current process to become a +process group leader, so we can only call setsid() once. Note that +the controlling terminal acquires the session leader's process +group when opened. + +Controlling terminals: +--------------------- + +B.7.1.1.3 p276: 'POSIX.1 does not specify a mechanism by which to +allocate a controlling terminal. This is normally done by a system +utility (such as 'getty') and is considered ... outside the scope +of POSIX.1.' It goes on to say that historically the first open() +of a tty in a session sets the controlling terminal. P130 has the +full details; nothing particularly surprising. + +The glossary p12 describes a 'controlling process' as the first +process in a session that acquires a controlling terminal. Access +to the terminal from the session is revoked if the controlling +process exits (see p50, in the discussion of process termination). + +Design notes: +------------ + +your generic finite state machine +we are fascist about which signals we elect to receive, + even signals purportedly generated by hardware +handle fatal errors gracefully if possible (we reboot if we goof!!) + if we get a segmentation fault etc., print a message on the console + and spin for a while before rebooting + (this at least decreases the amount of paper consumed :-) +apply hysteresis to rapidly exiting gettys +check wait status of children we reap + don't wait for stopped children +don't use SIGCHILD, it's too expensive + but it may close windows and avoid races, sigh +look for EINTR in case we need to change state +init is responsible for utmp and wtmp maintenance (ick) + maybe now we can consider replacements? maintain them in parallel + init only removes utmp and closes out wtmp entries... + +necessary states and state transitions (gleaned from the man page): + 1: single user shell (with password checking?); on exit, go to 2 + 2: rc script: on exit 0, go to 3; on exit N (error), go to 1 + 3: read ttys file: on completion, go to 4 + 4: multi-user operation: on SIGTERM, go to 7; on SIGHUP, go to 5; + on SIGTSTP, go to 6 + 5: clean up mode (re-read ttys file, killing off controlling processes + on lines that are now 'off', starting them on lines newly 'on') + on completion, go to 4 + 6: boring mode (no new sessions); signals as in 4 + 7: death: send SIGHUP to all controlling processes, reap for 30 seconds, + then go to 1 (warn if not all processes died, i.e. wait blocks) +Given the -s flag, we start at state 1; otherwise state 2 diff --git a/sbin/init/init.8 b/sbin/init/init.8 new file mode 100644 index 00000000000..13ebc69b55d --- /dev/null +++ b/sbin/init/init.8 @@ -0,0 +1,293 @@ +.\" $NetBSD: init.8,v 1.6 1995/03/18 14:56:31 cgd Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Donn Seeley at Berkeley Software Design, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)init.8 8.3 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt INIT 8 +.Os BSD 4 +.Sh NAME +.Nm init +.Nd process control initialization +.Sh SYNOPSIS +.Nm init +.Sh DESCRIPTION +The +.Nm init +program +is the last stage of the boot process. +It normally runs the automatic reboot sequence as described in +.Xr reboot 8 , +and if this succeeds, begins multi-user operation. +If the reboot scripts fail, +.Nm init +commences single user operation by giving +the super-user a shell on the console. +The +.Nm init +program may be passed parameters +from the boot program to +prevent the system from going multi-user and to instead execute +a single user shell without starting the normal daemons. +The system is then quiescent for maintenance work and may +later be made to go to multi-user by exiting the +single-user shell (with ^D). +This +causes +.Nm init +to run the +.Pa /etc/rc +start up command file in fastboot mode (skipping disk checks). +.Pp +If the +.Nm console +entry in the +.Xr ttys 5 +file is marked ``insecure'', +then +.Nm init +will require that the superuser password be +entered before the system will start a single-user shell. +The password check is skipped if the +.Nm console +is marked as ``secure''. +.Pp +The kernel runs with four different levels of security. +Any superuser process can raise the security level, but only +.Nm init +can lower it. +Security levels are defined as follows: +.Bl -tag -width flag +.It Ic -1 +Permanently insecure mode \- always run system in level 0 mode. +.It Ic 0 +Insecure mode \- immutable and append-only flags may be turned off. +All devices may be read or written subject to their permissions. +.It Ic 1 +Secure mode \- immutable and append-only flags may not be changed; +disks for mounted filesystems, +.Pa /dev/mem , +and +.Pa /dev/kmem +are read-only. +.It Ic 2 +Highly secure mode \- same as secure mode, plus disks are always +read-only whether mounted or not. +This level precludes tampering with filesystems by unmounting them, +but also inhibits running +.Xr newfs 8 +while the system is multi-user. +.El +.Pp +Normally, the system runs in level 0 mode while single user +and in level 1 mode while multiuser. +If the level 2 mode is desired while running multiuser, +it can be set in the startup script +.Pa /etc/rc +using +.Xr sysctl 8 . +If it is desired to run the system in level 0 mode while multiuser, +the administrator must build a kernel with the variable +.Nm securelevel +defined in the file +.Pa /sys/arch/compile/MACHINE/param.c +and initialize it to -1. +.Pp +In multi-user operation, +.Nm init +maintains +processes for the terminal ports found in the file +.Xr ttys 5 . +.Nm Init +reads this file, and executes the command found in the second field. +This command is usually +.Xr getty 8 ; +.Xr getty +opens and initializes the tty line +and +executes the +.Xr login +program. +The +.Xr login +program, when a valid user logs in, +executes a shell for that user. When this shell +dies, either because the user logged out +or an abnormal termination occurred (a signal), +the +.Nm init +program wakes up, deletes the user +from the +.Xr utmp 5 +file of current users and records the logout in the +.Xr wtmp +file. +The cycle is +then restarted by +.Nm init +executing a new +.Xr getty +for the line. +.Pp +Line status (on, off, secure, getty, or window information) +may be changed in the +.Xr ttys +file without a reboot by sending the signal +.Dv SIGHUP +to +.Nm init +with the command +.Dq Li "kill \-s HUP 1" . +On receipt of this signal, +.Nm init +re-reads the +.Xr ttys +file. +When a line is turned off in +.Xr ttys , +.Nm init +will send a SIGHUP signal to the controlling process +for the session associated with the line. +For any lines that were previously turned off in the +.Xr ttys +file and are now on, +.Nm init +executes a new +.Xr getty +to enable a new login. +If the getty or window field for a line is changed, +the change takes effect at the end of the current +login session (e.g., the next time +.Nm init +starts a process on the line). +If a line is commented out or deleted from +.Xr ttys , +.Nm init +will not do anything at all to that line. +However, it will complain that the relationship between lines +in the +.Xr ttys +file and records in the +.Xr utmp +file is out of sync, +so this practice is not recommended. +.Pp +.Nm Init +will terminate multi-user operations and resume single-user mode +if sent a terminate +.Pq Dv TERM +signal, for example, +.Dq Li "kill \-s TERM 1" . +If there are processes outstanding that are deadlocked (because of +hardware or software failure), +.Xr init +will not wait for them all to die (which might take forever), but +will time out after 30 seconds and print a warning message. +.Pp +.Nm Init +will cease creating new +.Xr getty Ns 's +and allow the system to slowly die away, if it is sent a terminal stop +.Pq Dv TSTP +signal, i.e. +.Dq Li "kill \-s TSTP 1" . +A later hangup will resume full +multi-user operations, or a terminate will start a single user shell. +This hook is used by +.Xr reboot 8 +and +.Xr halt 8 . +.Pp +The role of +.Nm init +is so critical that if it dies, the system will reboot itself +automatically. +If, at bootstrap time, the +.Xr init +process cannot be located, the system will panic with the message +``panic: "init died (signal %d, exit %d)''. +.Sh DIAGNOSTICS +.Bl -diag +.It "getty repeating too quickly on port %s, sleeping" +A process being started to service a line is exiting quickly +each time it is started. +This is often caused by a ringing or noisy terminal line. +.Em "Init will sleep for 10 seconds" , +.Em "then continue trying to start the process" . +.Pp +.It "some processes would not die; ps axl advised." +A process +is hung and could not be killed when the system was shutting down. +This condition is usually caused by a process +that is stuck in a device driver because of +a persistent device error condition. +.El +.Sh FILES +.Bl -tag -width /var/log/wtmp -compact +.It Pa /dev/console +System console device. +.It Pa /dev/tty* +Terminal ports found in +.Xr ttys . +.It Pa /var/run/utmp +Record of Current users on the system. +.It Pa /var/log/wtmp +Record of all logins and logouts. +.It Pa /etc/ttys +The terminal initialization information file. +.It Pa /etc/rc +System startup commands. +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr kill 1 , +.Xr sh 1 , +.Xr ttys 5 , +.Xr crash 8 , +.Xr getty 8 , +.Xr rc 8 , +.Xr reboot 8 , +.Xr halt 8 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +.Sh BUGS +Systems without +.Xr sysctl +behave as though they have security level \-1. diff --git a/sbin/init/init.c b/sbin/init/init.c new file mode 100644 index 00000000000..d0788c6826e --- /dev/null +++ b/sbin/init/init.c @@ -0,0 +1,1335 @@ +/* $NetBSD: init.c,v 1.21 1995/10/05 06:11:24 mycroft Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; +#else +static char rcsid[] = "$NetBSD: init.c,v 1.21 1995/10/05 06:11:24 mycroft Exp $"; +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/wait.h> + +#include <db.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <ttyent.h> +#include <unistd.h> + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#ifdef SECURE +#include <pwd.h> +#endif + +#include "pathnames.h" + +/* + * Until the mythical util.h arrives... + */ +extern int login_tty __P((int)); +extern int logout __P((const char *)); +extern void logwtmp __P((const char *, const char *, const char *)); + +/* + * Sleep times; used to prevent thrashing. + */ +#define GETTY_SPACING 5 /* N secs minimum getty spacing */ +#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ +#define WINDOW_WAIT 3 /* wait N secs after starting window */ +#define STALL_TIMEOUT 30 /* wait N secs after warning */ +#define DEATH_WATCH 10 /* wait N secs for procs to die */ + +void handle __P((sig_t, ...)); +void delset __P((sigset_t *, ...)); + +void stall __P((char *, ...)); +void warning __P((char *, ...)); +void emergency __P((char *, ...)); +void disaster __P((int)); +void badsys __P((int)); + +/* + * We really need a recursive typedef... + * The following at least guarantees that the return type of (*state_t)() + * is sufficiently wide to hold a function pointer. + */ +typedef long (*state_func_t) __P((void)); +typedef state_func_t (*state_t) __P((void)); + +state_func_t single_user __P((void)); +state_func_t runcom __P((void)); +state_func_t read_ttys __P((void)); +state_func_t multi_user __P((void)); +state_func_t clean_ttys __P((void)); +state_func_t catatonia __P((void)); +state_func_t death __P((void)); + +enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; + +void transition __P((state_t)); +#ifndef LETS_GET_SMALL +state_t requested_transition = runcom; +#else /* LETS_GET_SMALL */ +state_t requested_transition = single_user; +#endif /* LETS_GET_SMALL */ + +void setctty __P((char *)); + +typedef struct init_session { + int se_index; /* index of entry in ttys file */ + pid_t se_process; /* controlling process */ + time_t se_started; /* used to avoid thrashing */ + int se_flags; /* status of session */ +#define SE_SHUTDOWN 0x1 /* session won't be restarted */ +#define SE_PRESENT 0x2 /* session is in /etc/ttys */ + char *se_device; /* filename of port */ + char *se_getty; /* what to run on that port */ + char **se_getty_argv; /* pre-parsed argument array */ + char *se_window; /* window system (started only once) */ + char **se_window_argv; /* pre-parsed argument array */ + struct init_session *se_prev; + struct init_session *se_next; +} session_t; + +void free_session __P((session_t *)); +session_t *new_session __P((session_t *, int, struct ttyent *)); +session_t *sessions; + +char **construct_argv __P((char *)); +void start_window_system __P((session_t *)); +void collect_child __P((pid_t)); +pid_t start_getty __P((session_t *)); +void transition_handler __P((int)); +void alrm_handler __P((int)); +void setsecuritylevel __P((int)); +int getsecuritylevel __P((void)); +int setupargv __P((session_t *, struct ttyent *)); +int clang; + +void clear_session_logs __P((session_t *)); + +int start_session_db __P((void)); +void add_session __P((session_t *)); +void del_session __P((session_t *)); +session_t *find_session __P((pid_t)); +DB *session_db; + +/* + * The mother of all processes. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + struct sigaction sa; + sigset_t mask; + +#ifndef LETS_GET_SMALL + /* Dispose of random users. */ + if (getuid() != 0) { + (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); + exit (1); + } + + /* System V users like to reexec init. */ + if (getpid() != 1) { + (void)fprintf(stderr, "init: already running\n"); + exit (1); + } + + /* + * Note that this does NOT open a file... + * Does 'init' deserve its own facility number? + */ + openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); +#endif /* LETS_GET_SMALL */ + + /* + * Create an initial session. + */ + if (setsid() < 0) + warning("initial setsid() failed: %m"); + + /* + * Establish an initial user so that programs running + * single user do not freak out and die (like passwd). + */ + if (setlogin("root") < 0) + warning("setlogin() failed: %m"); + +#ifndef LETS_GET_SMALL + /* + * This code assumes that we always get arguments through flags, + * never through bits set in some random machine register. + */ + while ((c = getopt(argc, argv, "sf")) != -1) + switch (c) { + case 's': + requested_transition = single_user; + break; + case 'f': + runcom_mode = FASTBOOT; + break; + default: + warning("unrecognized flag '-%c'", c); + break; + } + + if (optind != argc) + warning("ignoring excess arguments"); +#else /* LETS_GET_SMALL */ + requested_transition = single_user; +#endif /* LETS_GET_SMALL */ + + /* + * We catch or block signals rather than ignore them, + * so that they get reset on exec. + */ + handle(badsys, SIGSYS, 0); + handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, + SIGBUS, SIGXCPU, SIGXFSZ, 0); + handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); + handle(alrm_handler, SIGALRM, 0); + sigfillset(&mask); + delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, + SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); + (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); + + /* + * Paranoia. + */ + close(0); + close(1); + close(2); + + /* + * Start the state machine. + */ + transition(requested_transition); + + /* + * Should never reach here. + */ + return 1; +} + +/* + * Associate a function with a signal handler. + */ +void +#ifdef __STDC__ +handle(sig_t handler, ...) +#else +handle(va_alist) + va_dcl +#endif +{ + int sig; + struct sigaction sa; + int mask_everything; + va_list ap; +#ifndef __STDC__ + sig_t handler; + + va_start(ap); + handler = va_arg(ap, sig_t); +#else + va_start(ap, handler); +#endif + + sa.sa_handler = handler; + sigfillset(&mask_everything); + + while (sig = va_arg(ap, int)) { + sa.sa_mask = mask_everything; + /* XXX SA_RESTART? */ + sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; + sigaction(sig, &sa, (struct sigaction *) 0); + } + va_end(ap); +} + +/* + * Delete a set of signals from a mask. + */ +void +#ifdef __STDC__ +delset(sigset_t *maskp, ...) +#else +delset(va_alist) + va_dcl +#endif +{ + int sig; + va_list ap; +#ifndef __STDC__ + sigset_t *maskp; + + va_start(ap); + maskp = va_arg(ap, sigset_t *); +#else + va_start(ap, maskp); +#endif + + while (sig = va_arg(ap, int)) + sigdelset(maskp, sig); + va_end(ap); +} + +/* + * Log a message and sleep for a while (to give someone an opportunity + * to read it and to save log or hardcopy output if the problem is chronic). + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +stall(char *message, ...) +#else +stall(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); + sleep(STALL_TIMEOUT); +} + +/* + * Like stall(), but doesn't sleep. + * If cpp had variadic macros, the two functions could be #defines for another. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +warning(char *message, ...) +#else +warning(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); +} + +/* + * Log an emergency message. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +emergency(char *message, ...) +#else +emergency(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_EMERG, message, ap); + va_end(ap); +} + +/* + * Catch a SIGSYS signal. + * + * These may arise if a system does not support sysctl. + * We tolerate up to 25 of these, then throw in the towel. + */ +void +badsys(sig) + int sig; +{ + static int badcount = 0; + + if (badcount++ < 25) + return; + disaster(sig); +} + +/* + * Catch an unexpected signal. + */ +void +disaster(sig) + int sig; +{ + emergency("fatal signal: %s", strsignal(sig)); + + sleep(STALL_TIMEOUT); + _exit(sig); /* reboot */ +} + +/* + * Get the security level of the kernel. + */ +int +getsecuritylevel() +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + size_t len; + extern int errno; + + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + len = sizeof curlevel; + if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { + emergency("cannot get kernel security level: %s", + strerror(errno)); + return (-1); + } + return (curlevel); +#else + return (-1); +#endif +} + +/* + * Set the security level of the kernel. + */ +void +setsecuritylevel(newlevel) + int newlevel; +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + extern int errno; + + curlevel = getsecuritylevel(); + if (newlevel == curlevel) + return; + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { + emergency( + "cannot change kernel security level from %d to %d: %s", + curlevel, newlevel, strerror(errno)); + return; + } +#ifdef SECURE + warning("kernel security level changed from %d to %d", + curlevel, newlevel); +#endif +#endif +} + +/* + * Change states in the finite state machine. + * The initial state is passed as an argument. + */ +void +transition(s) + state_t s; +{ + for (;;) + s = (state_t) (*s)(); +} + +/* + * Close out the accounting files for a login session. + * NB: should send a message to the session logger to avoid blocking. + */ +void +clear_session_logs(sp) + session_t *sp; +{ + char *line = sp->se_device + sizeof(_PATH_DEV) - 1; + + if (logout(line)) + logwtmp(line, "", ""); +} + +/* + * Start a session and allocate a controlling terminal. + * Only called by children of init after forking. + */ +void +setctty(name) + char *name; +{ + int fd; + + (void) revoke(name); + sleep (2); /* leave DTR low */ + if ((fd = open(name, O_RDWR)) == -1) { + stall("can't open %s: %m", name); + _exit(1); + } + if (login_tty(fd) == -1) { + stall("can't get %s for controlling terminal: %m", name); + _exit(1); + } +} + +/* + * Bring the system up single user. + */ +state_func_t +single_user() +{ + pid_t pid, wpid; + int status; + sigset_t mask; + char *shell = _PATH_BSHELL; + char *argv[2]; +#ifdef SECURE + struct ttyent *typ; + struct passwd *pp; + static const char banner[] = + "Enter root password, or ^D to go multi-user\n"; + char *clear, *password; +#endif + + /* + * If the kernel is in secure mode, downgrade it to insecure mode. + */ + if (getsecuritylevel() > 0) + setsecuritylevel(0); + + if ((pid = fork()) == 0) { + /* + * Start the single user session. + */ + setctty(_PATH_CONSOLE); + +#ifdef SECURE + /* + * Check the root password. + * We don't care if the console is 'on' by default; + * it's the only tty that can be 'off' and 'secure'. + */ + typ = getttynam("console"); + pp = getpwnam("root"); + if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { + write(2, banner, sizeof banner - 1); + for (;;) { + clear = getpass("Password:"); + if (clear == 0 || *clear == '\0') + _exit(0); + password = crypt(clear, pp->pw_passwd); + memset(clear, 0, _PASSWORD_LEN); + if (strcmp(password, pp->pw_passwd) == 0) + break; + warning("single-user login failed\n"); + } + } + endttyent(); + endpwent(); +#endif /* SECURE */ + +#ifdef DEBUGSHELL + { + char altshell[128], *cp = altshell; + int num; + +#define SHREQUEST \ + "Enter pathname of shell or RETURN for sh: " + (void)write(STDERR_FILENO, + SHREQUEST, sizeof(SHREQUEST) - 1); + while ((num = read(STDIN_FILENO, cp, 1)) != -1 && + num != 0 && *cp != '\n' && cp < &altshell[127]) + cp++; + *cp = '\0'; + if (altshell[0] != '\0') + shell = altshell; + } +#endif /* DEBUGSHELL */ + + /* + * Unblock signals. + * We catch all the interesting ones, + * and those are reset to SIG_DFL on exec. + */ + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + /* + * Fire off a shell. + * If the default one doesn't work, try the Bourne shell. + */ + argv[0] = "-sh"; + argv[1] = 0; + setenv("PATH", _PATH_STDPATH, 1); + execv(shell, argv); + emergency("can't exec %s for single user: %m", shell); + execv(_PATH_BSHELL, argv); + emergency("can't exec %s for single user: %m", _PATH_BSHELL); + sleep(STALL_TIMEOUT); + _exit(1); + } + + if (pid == -1) { + /* + * We are seriously hosed. Do our best. + */ + emergency("can't fork single-user shell, trying again"); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + return (state_func_t) single_user; + } + + requested_transition = 0; + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for single-user shell failed: %m; restarting"); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: shell stopped, restarting\n"); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid && !requested_transition); + + if (requested_transition) + return (state_func_t) requested_transition; + + if (!WIFEXITED(status)) { + if (WTERMSIG(status) == SIGKILL) { + /* + * reboot(8) killed shell? + */ + warning("single user shell terminated."); + sleep(STALL_TIMEOUT); + _exit(0); + } else { + warning("single user shell terminated, restarting"); + return (state_func_t) single_user; + } + } + + runcom_mode = FASTBOOT; +#ifndef LETS_GET_SMALL + return (state_func_t) runcom; +#else /* LETS_GET_SMALL */ + return (state_func_t) single_user; +#endif /* LETS_GET_SMALL */ +} + +#ifndef LETS_GET_SMALL +/* + * Run the system startup script. + */ +state_func_t +runcom() +{ + pid_t pid, wpid; + int status; + char *argv[4]; + struct sigaction sa; + + if ((pid = fork()) == 0) { + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); + (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); + + setctty(_PATH_CONSOLE); + + argv[0] = "sh"; + argv[1] = _PATH_RUNCOM; + argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; + argv[3] = 0; + + sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); + + execv(_PATH_BSHELL, argv); + stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); + _exit(1); /* force single user mode */ + } + + if (pid == -1) { + emergency("can't fork for %s on %s: %m", + _PATH_BSHELL, _PATH_RUNCOM); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + sleep(STALL_TIMEOUT); + return (state_func_t) single_user; + } + + /* + * Copied from single_user(). This is a bit paranoid. + */ + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for %s on %s failed: %m; going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: %s on %s stopped, restarting\n", + _PATH_BSHELL, _PATH_RUNCOM); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && + requested_transition == catatonia) { + /* /etc/rc executed /sbin/reboot; wait for the end quietly */ + sigset_t s; + + sigfillset(&s); + for (;;) + sigsuspend(&s); + } + + if (!WIFEXITED(status)) { + warning("%s on %s terminated abnormally, going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + + if (WEXITSTATUS(status)) + return (state_func_t) single_user; + + runcom_mode = AUTOBOOT; /* the default */ + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "reboot", ""); + return (state_func_t) read_ttys; +} + +/* + * Open the session database. + * + * NB: We could pass in the size here; is it necessary? + */ +int +start_session_db() +{ + if (session_db && (*session_db->close)(session_db)) + emergency("session database close: %s", strerror(errno)); + if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { + emergency("session database open: %s", strerror(errno)); + return (1); + } + return (0); + +} + +/* + * Add a new login session. + */ +void +add_session(sp) + session_t *sp; +{ + DBT key; + DBT data; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + data.data = &sp; + data.size = sizeof sp; + + if ((*session_db->put)(session_db, &key, &data, 0)) + emergency("insert %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Delete an old login session. + */ +void +del_session(sp) + session_t *sp; +{ + DBT key; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + + if ((*session_db->del)(session_db, &key, 0)) + emergency("delete %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Look up a login session by pid. + */ +session_t * +#ifdef __STDC__ +find_session(pid_t pid) +#else +find_session(pid) + pid_t pid; +#endif +{ + DBT key; + DBT data; + session_t *ret; + + key.data = &pid; + key.size = sizeof pid; + if ((*session_db->get)(session_db, &key, &data, 0) != 0) + return 0; + memcpy(&ret, data.data, sizeof(ret)); + return ret; +} + +/* + * Construct an argument vector from a command line. + */ +char ** +construct_argv(command) + char *command; +{ + register int argc = 0; + register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) + * sizeof (char *)); + static const char separators[] = " \t"; + + if ((argv[argc++] = strtok(command, separators)) == 0) + return 0; + while (argv[argc++] = strtok((char *) 0, separators)) + continue; + return argv; +} + +/* + * Deallocate a session descriptor. + */ +void +free_session(sp) + register session_t *sp; +{ + free(sp->se_device); + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + if (sp->se_window) { + free(sp->se_window); + free(sp->se_window_argv); + } + free(sp); +} + +/* + * Allocate a new session descriptor. + */ +session_t * +new_session(sprev, session_index, typ) + session_t *sprev; + int session_index; + register struct ttyent *typ; +{ + register session_t *sp; + + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_name == 0 || + typ->ty_getty == 0) + return 0; + + sp = (session_t *) malloc(sizeof (session_t)); + memset(sp, 0, sizeof *sp); + + sp->se_flags = SE_PRESENT; + sp->se_index = session_index; + + sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); + (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); + + if (setupargv(sp, typ) == 0) { + free_session(sp); + return (0); + } + + sp->se_next = 0; + if (sprev == 0) { + sessions = sp; + sp->se_prev = 0; + } else { + sprev->se_next = sp; + sp->se_prev = sprev; + } + + return sp; +} + +/* + * Calculate getty and if useful window argv vectors. + */ +int +setupargv(sp, typ) + session_t *sp; + struct ttyent *typ; +{ + + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); + (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); + sp->se_getty_argv = construct_argv(sp->se_getty); + if (sp->se_getty_argv == 0) { + warning("can't parse getty for port %s", sp->se_device); + free(sp->se_getty); + sp->se_getty = 0; + return (0); + } + if (typ->ty_window) { + if (sp->se_window) + free(sp->se_window); + sp->se_window = strdup(typ->ty_window); + sp->se_window_argv = construct_argv(sp->se_window); + if (sp->se_window_argv == 0) { + warning("can't parse window for port %s", + sp->se_device); + free(sp->se_window); + sp->se_window = 0; + return (0); + } + } + return (1); +} + +/* + * Walk the list of ttys and create sessions for each active line. + */ +state_func_t +read_ttys() +{ + int session_index = 0; + register session_t *sp, *snext; + register struct ttyent *typ; + + /* + * Destroy any previous session state. + * There shouldn't be any, but just in case... + */ + for (sp = sessions; sp; sp = snext) { + if (sp->se_process) + clear_session_logs(sp); + snext = sp->se_next; + free_session(sp); + } + sessions = 0; + if (start_session_db()) + return (state_func_t) single_user; + + /* + * Allocate a session entry for each active port. + * Note that sp starts at 0. + */ + while (typ = getttyent()) + if (snext = new_session(sp, ++session_index, typ)) + sp = snext; + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Start a window system running. + */ +void +start_window_system(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + + if ((pid = fork()) == -1) { + emergency("can't fork for window system on port %s: %m", + sp->se_device); + /* hope that getty fails and we can try again */ + return; + } + + if (pid) + return; + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + if (setsid() < 0) + emergency("setsid failed (window) %m"); + + execv(sp->se_window_argv[0], sp->se_window_argv); + stall("can't exec window system '%s' for port %s: %m", + sp->se_window_argv[0], sp->se_device); + _exit(1); +} + +/* + * Start a login session running. + */ +pid_t +start_getty(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + time_t current_time = time((time_t *) 0); + + /* + * fork(), not vfork() -- we can't afford to block. + */ + if ((pid = fork()) == -1) { + emergency("can't fork for getty on port %s: %m", sp->se_device); + return -1; + } + + if (pid) + return pid; + + if (current_time > sp->se_started && + current_time - sp->se_started < GETTY_SPACING) { + warning("getty repeating too quickly on port %s, sleeping", + sp->se_device); + sleep((unsigned) GETTY_SLEEP); + } + + if (sp->se_window) { + start_window_system(sp); + sleep(WINDOW_WAIT); + } + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + execv(sp->se_getty_argv[0], sp->se_getty_argv); + stall("can't exec getty '%s' for port %s: %m", + sp->se_getty_argv[0], sp->se_device); + _exit(1); +} +#endif /* LETS_GET_SMALL */ + +/* + * Collect exit status for a child. + * If an exiting login, start a new login running. + */ +void +#ifdef __STDC__ +collect_child(pid_t pid) +#else +collect_child(pid) + pid_t pid; +#endif +{ +#ifndef LETS_GET_SMALL + register session_t *sp, *sprev, *snext; + + if (! sessions) + return; + + if (! (sp = find_session(pid))) + return; + + clear_session_logs(sp); + del_session(sp); + sp->se_process = 0; + + if (sp->se_flags & SE_SHUTDOWN) { + if (sprev = sp->se_prev) + sprev->se_next = sp->se_next; + else + sessions = sp->se_next; + if (snext = sp->se_next) + snext->se_prev = sp->se_prev; + free_session(sp); + return; + } + + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + return; + } + + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); +#endif /* LETS_GET_SMALL */ +} + +/* + * Catch a signal and request a state transition. + */ +void +transition_handler(sig) + int sig; +{ + + switch (sig) { +#ifndef LETS_GET_SMALL + case SIGHUP: + requested_transition = clean_ttys; + break; + case SIGTERM: + requested_transition = death; + break; + case SIGTSTP: + requested_transition = catatonia; + break; +#endif /* LETS_GET_SMALL */ + default: + requested_transition = 0; + break; + } +} + +#ifndef LETS_GET_SMALL +/* + * Take the system multiuser. + */ +state_func_t +multi_user() +{ + pid_t pid; + register session_t *sp; + + requested_transition = 0; + + /* + * If the administrator has not set the security level to -1 + * to indicate that the kernel should not run multiuser in secure + * mode, and the run script has not set a higher level of security + * than level 1, then put the kernel into secure mode. + */ + if (getsecuritylevel() == 0) + setsecuritylevel(1); + + for (sp = sessions; sp; sp = sp->se_next) { + if (sp->se_process) + continue; + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + break; + } + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); + } + + while (!requested_transition) + if ((pid = waitpid(-1, (int *) 0, 0)) != -1) + collect_child(pid); + + return (state_func_t) requested_transition; +} + +/* + * This is an n-squared algorithm. We hope it isn't run often... + */ +state_func_t +clean_ttys() +{ + register session_t *sp, *sprev; + register struct ttyent *typ; + register int session_index = 0; + register int devlen; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags &= ~SE_PRESENT; + + devlen = sizeof(_PATH_DEV) - 1; + while (typ = getttyent()) { + ++session_index; + + for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) + if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) + break; + + if (sp) { + sp->se_flags |= SE_PRESENT; + if (sp->se_index != session_index) { + warning("port %s changed utmp index from %d to %d", + sp->se_device, sp->se_index, + session_index); + sp->se_index = session_index; + } + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_getty == 0) { + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + continue; + } + sp->se_flags &= ~SE_SHUTDOWN; + if (setupargv(sp, typ) == 0) { + warning("can't parse getty for port %s", + sp->se_device); + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + } + continue; + } + + new_session(sprev, session_index, typ); + } + + endttyent(); + + for (sp = sessions; sp; sp = sp->se_next) + if ((sp->se_flags & SE_PRESENT) == 0) { + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + } + + return (state_func_t) multi_user; +} + +/* + * Block further logins. + */ +state_func_t +catatonia() +{ + register session_t *sp; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + return (state_func_t) multi_user; +} +#endif /* LETS_GET_SMALL */ + +/* + * Note SIGALRM. + */ +void +alrm_handler(sig) + int sig; +{ + clang = 1; +} + +#ifndef LETS_GET_SMALL +/* + * Bring the system down to single user. + */ +state_func_t +death() +{ + register session_t *sp; + register int i; + pid_t pid; + static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "shutdown", ""); + + for (i = 0; i < 3; ++i) { + if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) + return (state_func_t) single_user; + + clang = 0; + alarm(DEATH_WATCH); + do + if ((pid = waitpid(-1, (int *)0, 0)) != -1) + collect_child(pid); + while (clang == 0 && errno != ECHILD); + + if (errno == ECHILD) + return (state_func_t) single_user; + } + + warning("some processes would not die; ps axl advised"); + + return (state_func_t) single_user; +} +#endif /* LETS_GET_SMALL */ diff --git a/sbin/init/pathnames.h b/sbin/init/pathnames.h new file mode 100644 index 00000000000..0076c391b91 --- /dev/null +++ b/sbin/init/pathnames.h @@ -0,0 +1,44 @@ +/* $NetBSD: pathnames.h,v 1.5 1995/03/18 14:56:35 cgd Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ + +#include <paths.h> + +#define _PATH_SLOGGER "/sbin/session_logger" +#define _PATH_RUNCOM "/etc/rc" |