xref: /onnv-gate/usr/src/cmd/utmpd/utmpd.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate /*
31*0Sstevel@tonic-gate  * Portions of such source code were derived from Berkeley 4.3 BSD
32*0Sstevel@tonic-gate  * under license from the Regents of the University of California.
33*0Sstevel@tonic-gate  */
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
36*0Sstevel@tonic-gate 
37*0Sstevel@tonic-gate /*
38*0Sstevel@tonic-gate  * utmpd	- utmp daemon
39*0Sstevel@tonic-gate  *
40*0Sstevel@tonic-gate  *		This program receives requests from  pututxline(3)
41*0Sstevel@tonic-gate  *		via a named pipe to watch the process to make sure it cleans up
42*0Sstevel@tonic-gate  *		its utmpx entry on termination.
43*0Sstevel@tonic-gate  *		The program keeps a list of procs
44*0Sstevel@tonic-gate  *		and uses poll() on their /proc files to detect termination.
45*0Sstevel@tonic-gate  *		Also the  program periodically scans the /etc/utmpx file for
46*0Sstevel@tonic-gate  *		processes that aren't in the table so they can be watched.
47*0Sstevel@tonic-gate  *
48*0Sstevel@tonic-gate  *		If utmpd doesn't hear back over the pipe from pututline(3) that
49*0Sstevel@tonic-gate  *		the process has removed its entry it cleans the entry when the
50*0Sstevel@tonic-gate  *		the process terminates.
51*0Sstevel@tonic-gate  *		The AT&T Copyright above is there since we borrowed the pipe
52*0Sstevel@tonic-gate  *		mechanism from init(1m).
53*0Sstevel@tonic-gate  */
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate #include	<sys/types.h>
57*0Sstevel@tonic-gate #include	<signal.h>
58*0Sstevel@tonic-gate #include	<stdio.h>
59*0Sstevel@tonic-gate #include	<unistd.h>
60*0Sstevel@tonic-gate #include	<utmpx.h>
61*0Sstevel@tonic-gate #include	<errno.h>
62*0Sstevel@tonic-gate #include	<termio.h>
63*0Sstevel@tonic-gate #include	<sys/termios.h>
64*0Sstevel@tonic-gate #include	<sys/tty.h>
65*0Sstevel@tonic-gate #include	<ctype.h>
66*0Sstevel@tonic-gate #include	<sys/stat.h>
67*0Sstevel@tonic-gate #include	<sys/statvfs.h>
68*0Sstevel@tonic-gate #include	<fcntl.h>
69*0Sstevel@tonic-gate #include	<time.h>
70*0Sstevel@tonic-gate #include	<sys/stropts.h>
71*0Sstevel@tonic-gate #include	<wait.h>
72*0Sstevel@tonic-gate #include	<syslog.h>
73*0Sstevel@tonic-gate #include	<stdlib.h>
74*0Sstevel@tonic-gate #include	<string.h>
75*0Sstevel@tonic-gate #include	<poll.h>
76*0Sstevel@tonic-gate #include	<deflt.h>
77*0Sstevel@tonic-gate #include	<procfs.h>
78*0Sstevel@tonic-gate #include	<sys/resource.h>
79*0Sstevel@tonic-gate 
80*0Sstevel@tonic-gate #define	dprintf(x)	if (Debug) (void) printf x
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate /*
83*0Sstevel@tonic-gate  * Memory allocation keyed off MAX_FDS
84*0Sstevel@tonic-gate  */
85*0Sstevel@tonic-gate #define	MAX_FDS		4064	/* Maximum # file descriptors */
86*0Sstevel@tonic-gate #define	EXTRA_MARGIN	32	/* Allocate this many more FDS over Max_Fds */
87*0Sstevel@tonic-gate /*
88*0Sstevel@tonic-gate  * MAX_POLLNV & RESETS - paranoia to cover an error case that might not exist
89*0Sstevel@tonic-gate  */
90*0Sstevel@tonic-gate #define	MAX_POLL_ERRS	1024	/* Count of bad errors */
91*0Sstevel@tonic-gate #define	MAX_RESETS	1024	/* Maximum times to reload tables */
92*0Sstevel@tonic-gate #define	POLL_TIMEOUT	300	/* Default Timeout for poll() in seconds */
93*0Sstevel@tonic-gate #define	CLEANIT		1	/* Used by rem_pid() */
94*0Sstevel@tonic-gate #define	DONT_CLEAN	0	/* Used by rem_pid() */
95*0Sstevel@tonic-gate #define	UTMP_DEFAULT	"/etc/default/utmpd"
96*0Sstevel@tonic-gate #define	WARN_TIME	3600	/* seconds between utmp checks */
97*0Sstevel@tonic-gate #define	WTMPX_UFREQ	60	/* seconds between updating WTMPX's atime */
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate /*
101*0Sstevel@tonic-gate  * The pidrec structure describes the data shipped down the pipe to
102*0Sstevel@tonic-gate  * us from the pututxline() library in
103*0Sstevel@tonic-gate  * lib/libc/port/gen/getutx.c
104*0Sstevel@tonic-gate  */
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate /*
107*0Sstevel@tonic-gate  * pd_type's
108*0Sstevel@tonic-gate  */
109*0Sstevel@tonic-gate #define	ADDPID  1
110*0Sstevel@tonic-gate #define	REMPID  2
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate struct  pidrec {
113*0Sstevel@tonic-gate 	int	pd_type;		/* Command type */
114*0Sstevel@tonic-gate 	pid_t	pd_pid;			/* pid to add or remove */
115*0Sstevel@tonic-gate };
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate /*
119*0Sstevel@tonic-gate  * Since this program uses poll(2) and poll takes an array of file descriptors
120*0Sstevel@tonic-gate  * as an argument we maintain our data in tables.
121*0Sstevel@tonic-gate  * One table is the file descriptor array for poll, another parallel
122*0Sstevel@tonic-gate  * array is a table which contains the process ID of the corresponding
123*0Sstevel@tonic-gate  * open fd.  These tables are kept sorted by process ID for quick lookups.
124*0Sstevel@tonic-gate  */
125*0Sstevel@tonic-gate 
126*0Sstevel@tonic-gate struct  pidentry {
127*0Sstevel@tonic-gate 	pid_t	pl_pid;			/* pid to watch for */
128*0Sstevel@tonic-gate 	int 	pl_status;		/* Exit status of proc */
129*0Sstevel@tonic-gate };
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate static struct pidentry *pidtable = NULL;
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate static pollfd_t *fdtable = NULL;
134*0Sstevel@tonic-gate 
135*0Sstevel@tonic-gate static int	pidcnt = 0;		/* Number of procs being watched */
136*0Sstevel@tonic-gate static char	*prog_name;		/* To save the invocation name away */
137*0Sstevel@tonic-gate static char	*UTMPPIPE_DIR =	"/etc";
138*0Sstevel@tonic-gate static char	*UTMPPIPE = "/etc/utmppipe";
139*0Sstevel@tonic-gate static int	Pfd = -1;		/* File descriptor of named pipe */
140*0Sstevel@tonic-gate static int 	Poll_timeout = POLL_TIMEOUT;
141*0Sstevel@tonic-gate static int	WTMPXfd = -1;		/* File descriptor of WTMPX_FILE */
142*0Sstevel@tonic-gate static int	WTMPX_ufreq = WTMPX_UFREQ;
143*0Sstevel@tonic-gate static int	Debug = 0;		/* Set by command line argument */
144*0Sstevel@tonic-gate static int	Max_fds		= MAX_FDS;
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate /*
147*0Sstevel@tonic-gate  * This program has three main components plus utilities and debug routines
148*0Sstevel@tonic-gate  *	Receiver - receives the process ID or process for us to watch.
149*0Sstevel@tonic-gate  *		   (Uses a named pipe to get messages)
150*0Sstevel@tonic-gate  *	Watcher	 - Use poll(2) to watch for processes to die so they
151*0Sstevel@tonic-gate  *		   can be cleaned up (get marked as DEAD_PROCESS)
152*0Sstevel@tonic-gate  *	Scanner  - periodically scans the utmpx file for stale entries
153*0Sstevel@tonic-gate  *		   or live entries that we don't know about.
154*0Sstevel@tonic-gate  */
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate static int wait_for_pids();	/* Watcher - uses poll */
157*0Sstevel@tonic-gate static void scan_utmps();	/* Scanner, reads utmpx file */
158*0Sstevel@tonic-gate static void drain_pipe();	/* Receiver - reads mesgs over UTMPPIPE */
159*0Sstevel@tonic-gate static void setup_pipe();	/* For setting up receiver */
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate static void add_pid();		/* Adds a process to the table */
162*0Sstevel@tonic-gate static void rem_pid();		/* Removes a process from the table */
163*0Sstevel@tonic-gate static int find_pid();		/* Finds a process in the table */
164*0Sstevel@tonic-gate static int proc_to_fd();	/* Takes a pid and returns an fd for its proc */
165*0Sstevel@tonic-gate static void load_tables();	/* Loads up the tables the first time around */
166*0Sstevel@tonic-gate static int pidcmp();		/* For sorting pids */
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate static void clean_entry();	/* Removes entry from our table and calls ... */
169*0Sstevel@tonic-gate static void clean_utmpx_ent();	/* Cleans a utmpx entry */
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate static void fatal();		/* Prints error message and calls exit */
172*0Sstevel@tonic-gate static void nonfatal();		/* Prints error message */
173*0Sstevel@tonic-gate static void print_tables();	/* Prints out internal tables for Debug */
174*0Sstevel@tonic-gate static int proc_is_alive(pid_t pid);	/* Check if a process is alive */
175*0Sstevel@tonic-gate static void warn_utmp(void);
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate /*
178*0Sstevel@tonic-gate  * main()  - Main does basic setup and calls wait_for_pids() to do the work
179*0Sstevel@tonic-gate  */
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate void
182*0Sstevel@tonic-gate main(argc, argv)
183*0Sstevel@tonic-gate 	char **argv;
184*0Sstevel@tonic-gate {
185*0Sstevel@tonic-gate 	char *defp;
186*0Sstevel@tonic-gate 	struct rlimit rlim;
187*0Sstevel@tonic-gate 	char tstr[80];
188*0Sstevel@tonic-gate 	int i;
189*0Sstevel@tonic-gate 	time_t curtime, now;
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate 	prog_name = argv[0];			/* Save invocation name */
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	if (getuid() != 0)  {
194*0Sstevel@tonic-gate 		(void) fprintf(stderr,
195*0Sstevel@tonic-gate 			"You must be root to run this program\n");
196*0Sstevel@tonic-gate 		fatal("You must be root to run this program");
197*0Sstevel@tonic-gate 	}
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate 	if (argc > 1) {
200*0Sstevel@tonic-gate 		if ((argc == 2 && (int)strlen(argv[1]) >= 2) &&
201*0Sstevel@tonic-gate 		    (argv[1][0] == '-' && argv[1][1] == 'd')) {
202*0Sstevel@tonic-gate 			Debug = 1;
203*0Sstevel@tonic-gate 		} else {
204*0Sstevel@tonic-gate 			(void) fprintf(stderr,
205*0Sstevel@tonic-gate 				"%s: Wrong number of arguments\n", prog_name);
206*0Sstevel@tonic-gate 			(void) fprintf(stderr,
207*0Sstevel@tonic-gate 				"Usage: %s [-debug]\n", prog_name);
208*0Sstevel@tonic-gate 			exit(-1);
209*0Sstevel@tonic-gate 		}
210*0Sstevel@tonic-gate 	}
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	/*
213*0Sstevel@tonic-gate 	 * Read defaults file for poll timeout
214*0Sstevel@tonic-gate 	 */
215*0Sstevel@tonic-gate 	if (defopen(UTMP_DEFAULT) == 0) {
216*0Sstevel@tonic-gate 		if ((defp = defread("SCAN_PERIOD=")) != NULL) {
217*0Sstevel@tonic-gate 			Poll_timeout = atol(defp);
218*0Sstevel@tonic-gate 			dprintf(("Poll timeout set to %d\n", Poll_timeout));
219*0Sstevel@tonic-gate 		}
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 		if ((defp = defread("WTMPX_UPDATE_FREQ=")) != NULL) {
222*0Sstevel@tonic-gate 			WTMPX_ufreq = atol(defp);
223*0Sstevel@tonic-gate 			dprintf(("WTMPX update frequency set to %d\n",
224*0Sstevel@tonic-gate 				    WTMPX_ufreq));
225*0Sstevel@tonic-gate 		}
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate 		/*
228*0Sstevel@tonic-gate 		 * Paranoia - if polling on large number of FDs is expensive /
229*0Sstevel@tonic-gate 		 * buggy the number can be set lower in the field.
230*0Sstevel@tonic-gate 		 */
231*0Sstevel@tonic-gate 		if ((defp = defread("MAX_FDS=")) != NULL) {
232*0Sstevel@tonic-gate 			Max_fds = atol(defp);
233*0Sstevel@tonic-gate 			dprintf(("Max_fds set to %d\n", Max_fds));
234*0Sstevel@tonic-gate 		}
235*0Sstevel@tonic-gate 		(void) defopen((char *)NULL);
236*0Sstevel@tonic-gate 	}
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate 	if (Debug == 0) {
239*0Sstevel@tonic-gate 		/*
240*0Sstevel@tonic-gate 		 * Daemonize ourselves
241*0Sstevel@tonic-gate 		 */
242*0Sstevel@tonic-gate 		if (fork()) {
243*0Sstevel@tonic-gate 			exit(0);
244*0Sstevel@tonic-gate 		}
245*0Sstevel@tonic-gate 		(void) close(0);
246*0Sstevel@tonic-gate 		(void) close(1);
247*0Sstevel@tonic-gate 		(void) close(2);
248*0Sstevel@tonic-gate 		/*
249*0Sstevel@tonic-gate 		 * We open these to avoid accidentally writing to a proc file
250*0Sstevel@tonic-gate 		 */
251*0Sstevel@tonic-gate 		(void) open("/dev/null", O_RDONLY);
252*0Sstevel@tonic-gate 		(void) open("/dev/null", O_WRONLY);
253*0Sstevel@tonic-gate 		(void) open("/dev/null", O_WRONLY);
254*0Sstevel@tonic-gate 		(void) setsid();		/* release process from tty */
255*0Sstevel@tonic-gate 	}
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 	openlog(prog_name, LOG_PID, LOG_DAEMON);	/* For error messages */
258*0Sstevel@tonic-gate 	warn_utmp();	/* check to see if utmp came back by accident */
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 	/*
261*0Sstevel@tonic-gate 	 * Allocate the pidtable and fdtable.  An earlier version did
262*0Sstevel@tonic-gate 	 * this as we go, but this is simpler.
263*0Sstevel@tonic-gate 	 */
264*0Sstevel@tonic-gate 	if ((pidtable = malloc(Max_fds * sizeof (struct pidentry))) == NULL)
265*0Sstevel@tonic-gate 		fatal("Malloc failed");
266*0Sstevel@tonic-gate 	if ((fdtable = malloc(Max_fds * sizeof (pollfd_t))) == NULL)
267*0Sstevel@tonic-gate 		fatal("Malloc failed");
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 	/*
270*0Sstevel@tonic-gate 	 * Up the limit on FDs
271*0Sstevel@tonic-gate 	 */
272*0Sstevel@tonic-gate 	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
273*0Sstevel@tonic-gate 		rlim.rlim_cur = Max_fds + EXTRA_MARGIN + 1;
274*0Sstevel@tonic-gate 		rlim.rlim_max = Max_fds + EXTRA_MARGIN + 1;
275*0Sstevel@tonic-gate 		if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
276*0Sstevel@tonic-gate 			fatal("Out of File Descriptors");
277*0Sstevel@tonic-gate 		}
278*0Sstevel@tonic-gate 	} else
279*0Sstevel@tonic-gate 		fatal("getrlimit returned failure");
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate 	if ((WTMPXfd = open(WTMPX_FILE, O_RDONLY)) < 0)
282*0Sstevel@tonic-gate 		nonfatal("WARNING: unable to open " WTMPX_FILE "for update.");
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 	/*
285*0Sstevel@tonic-gate 	 * Loop here scanning the utmpx file and waiting for processes
286*0Sstevel@tonic-gate 	 * to terminate.  Most of the activity is directed out of wait_for_pids.
287*0Sstevel@tonic-gate 	 * If wait_for_pids fails we reload the table and try again.
288*0Sstevel@tonic-gate 	 */
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate 	curtime = time(NULL);
291*0Sstevel@tonic-gate 	dprintf(("utmp warning timer set to %d seconds\n", WARN_TIME));
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 	for (i = 0; i < MAX_RESETS; i++) {
294*0Sstevel@tonic-gate 		load_tables();
295*0Sstevel@tonic-gate 		while (wait_for_pids() == 1) {
296*0Sstevel@tonic-gate 			now = time(NULL);
297*0Sstevel@tonic-gate 			if ((now - curtime) >= WARN_TIME) {
298*0Sstevel@tonic-gate 				dprintf(("utmp warning timer expired\n"));
299*0Sstevel@tonic-gate 				warn_utmp();
300*0Sstevel@tonic-gate 				curtime = now;
301*0Sstevel@tonic-gate 			}
302*0Sstevel@tonic-gate 		}
303*0Sstevel@tonic-gate 	}
304*0Sstevel@tonic-gate 
305*0Sstevel@tonic-gate 	(void) close(WTMPXfd);
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	/*
308*0Sstevel@tonic-gate 	 * We only get here if we had a bunch of resets - so give up
309*0Sstevel@tonic-gate 	 */
310*0Sstevel@tonic-gate 	fatal("Too many resets, giving up");
311*0Sstevel@tonic-gate }
312*0Sstevel@tonic-gate 
313*0Sstevel@tonic-gate /*
314*0Sstevel@tonic-gate  * load_tables()	- Designed to be called repeatedly if we need to
315*0Sstevel@tonic-gate  *			  restart things.  Zeros the pidcount, and loads
316*0Sstevel@tonic-gate  *			  the tables by scanning utmpx
317*0Sstevel@tonic-gate  */
318*0Sstevel@tonic-gate 
319*0Sstevel@tonic-gate static void
320*0Sstevel@tonic-gate load_tables()
321*0Sstevel@tonic-gate {
322*0Sstevel@tonic-gate 	int i;
323*0Sstevel@tonic-gate 
324*0Sstevel@tonic-gate 	dprintf(("Load tables\n"));
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate 	/*
327*0Sstevel@tonic-gate 	 * Close any open files.
328*0Sstevel@tonic-gate 	 */
329*0Sstevel@tonic-gate 	for (i = 0; i < pidcnt; i++)
330*0Sstevel@tonic-gate 		(void) close(fdtable[i].fd);
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 	pidcnt = 0;
333*0Sstevel@tonic-gate 	Pfd = -1;
334*0Sstevel@tonic-gate 	setup_pipe();		/* Setup the pipe to receive messages */
335*0Sstevel@tonic-gate 	scan_utmps();		/* Read in USER procs entries to watch */
336*0Sstevel@tonic-gate }
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate /*
340*0Sstevel@tonic-gate  *			*** The Watcher ***
341*0Sstevel@tonic-gate  *
342*0Sstevel@tonic-gate  * Wait_for_pids	- wait for the termination of a process in the table.
343*0Sstevel@tonic-gate  *			  Returns 1 on normal exist, 0 on failure.
344*0Sstevel@tonic-gate  */
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate static int
347*0Sstevel@tonic-gate wait_for_pids()
348*0Sstevel@tonic-gate {
349*0Sstevel@tonic-gate 	register struct pollfd *pfd;
350*0Sstevel@tonic-gate 	register int i;
351*0Sstevel@tonic-gate 	pid_t pid;
352*0Sstevel@tonic-gate 	int ret_val;
353*0Sstevel@tonic-gate 	int timeout;
354*0Sstevel@tonic-gate 	static time_t last_timeout  = 0;
355*0Sstevel@tonic-gate 	static int bad_error  = 0;	/* Count of POLL errors */
356*0Sstevel@tonic-gate 
357*0Sstevel@tonic-gate 	/*
358*0Sstevel@tonic-gate 	 * First time through we initialize last_timeout to now.
359*0Sstevel@tonic-gate 	 */
360*0Sstevel@tonic-gate 	if (last_timeout == 0)
361*0Sstevel@tonic-gate 		last_timeout = time(NULL);
362*0Sstevel@tonic-gate 
363*0Sstevel@tonic-gate 	/*
364*0Sstevel@tonic-gate 	 * Recalculate timeout - checking to see if time expired.
365*0Sstevel@tonic-gate 	 */
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	if ((timeout = Poll_timeout - (time(NULL) - last_timeout)) <= 0) {
368*0Sstevel@tonic-gate 		timeout = Poll_timeout;
369*0Sstevel@tonic-gate 		last_timeout = time(NULL);
370*0Sstevel@tonic-gate 		scan_utmps();
371*0Sstevel@tonic-gate 	}
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate 	fdtable[0].events = POLLRDNORM;
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate 	for (i = 0; i < (timeout / WTMPX_ufreq); i++) {
376*0Sstevel@tonic-gate 
377*0Sstevel@tonic-gate 		/*
378*0Sstevel@tonic-gate 		 * Loop here while getting EAGAIN
379*0Sstevel@tonic-gate 		 */
380*0Sstevel@tonic-gate 
381*0Sstevel@tonic-gate 		while ((ret_val = poll(fdtable, pidcnt, WTMPX_ufreq*1000)) < 0)
382*0Sstevel@tonic-gate 			if (errno == EAGAIN)
383*0Sstevel@tonic-gate 				(void) sleep(2);
384*0Sstevel@tonic-gate 			else
385*0Sstevel@tonic-gate 				fatal("poll");
386*0Sstevel@tonic-gate 		/*
387*0Sstevel@tonic-gate 		 * The results of pread(2) are discarded; we only want
388*0Sstevel@tonic-gate 		 * to update the access time of WTMPX_FILE.
389*0Sstevel@tonic-gate 		 * Periodically touching WTMPX helps determine when the
390*0Sstevel@tonic-gate 		 * OS became unavailable when the OS boots again .
391*0Sstevel@tonic-gate 		 * See PSARC 2004/462 for more information.
392*0Sstevel@tonic-gate 		 */
393*0Sstevel@tonic-gate 
394*0Sstevel@tonic-gate 		(void) pread(WTMPXfd, (void *)&pid, sizeof (pid), 0);
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate 		if (ret_val)		/* file descriptor(s) need attention */
397*0Sstevel@tonic-gate 			break;
398*0Sstevel@tonic-gate 	}
399*0Sstevel@tonic-gate 
400*0Sstevel@tonic-gate 	/*
401*0Sstevel@tonic-gate 	 * If ret_val == 0 the poll timed out - reset last_time and
402*0Sstevel@tonic-gate 	 * call scan_utmps
403*0Sstevel@tonic-gate 	 */
404*0Sstevel@tonic-gate 	if (ret_val == 0) {
405*0Sstevel@tonic-gate 		last_timeout = time(NULL);
406*0Sstevel@tonic-gate 		scan_utmps();
407*0Sstevel@tonic-gate 		return (1);
408*0Sstevel@tonic-gate 	}
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 	/*
411*0Sstevel@tonic-gate 	 * Check the pipe file descriptor
412*0Sstevel@tonic-gate 	 */
413*0Sstevel@tonic-gate 	if (fdtable[0].revents & POLLRDNORM) {
414*0Sstevel@tonic-gate 		drain_pipe();
415*0Sstevel@tonic-gate 		fdtable[0].revents = 0;
416*0Sstevel@tonic-gate 		ret_val--;
417*0Sstevel@tonic-gate 	}
418*0Sstevel@tonic-gate 
419*0Sstevel@tonic-gate 	(void) sleep(5);	/* Give parents time to cleanup children */
420*0Sstevel@tonic-gate 
421*0Sstevel@tonic-gate 	/*
422*0Sstevel@tonic-gate 	 * We got here because the status of one of the pids that
423*0Sstevel@tonic-gate 	 * we are polling on has changed, so search the table looking
424*0Sstevel@tonic-gate 	 * for the entry.
425*0Sstevel@tonic-gate 	 *
426*0Sstevel@tonic-gate 	 * The table is scanned backwards so that entries can be removed
427*0Sstevel@tonic-gate 	 * while we go since the table is compacted from high down to low
428*0Sstevel@tonic-gate 	 */
429*0Sstevel@tonic-gate 	for (i = pidcnt - 1; i > 0; i--) {
430*0Sstevel@tonic-gate 		/*
431*0Sstevel@tonic-gate 		 * Break out of the loop if we've processed all the entries.
432*0Sstevel@tonic-gate 		 */
433*0Sstevel@tonic-gate 		if (ret_val == 0)
434*0Sstevel@tonic-gate 			break;
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 		pfd = &fdtable[i];
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate 		if (pfd->fd < 0) {
439*0Sstevel@tonic-gate 			rem_pid((pid_t)0, i, DONT_CLEAN);
440*0Sstevel@tonic-gate 			continue;
441*0Sstevel@tonic-gate 		}
442*0Sstevel@tonic-gate 		/*
443*0Sstevel@tonic-gate 		 * POLLHUP	- Process terminated
444*0Sstevel@tonic-gate 		 */
445*0Sstevel@tonic-gate 		if (pfd->revents & POLLHUP) {
446*0Sstevel@tonic-gate 			psinfo_t psinfo;
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate 			if (pread(pfd->fd, &psinfo, sizeof (psinfo), (off_t)0)
449*0Sstevel@tonic-gate 			    != sizeof (psinfo)) {
450*0Sstevel@tonic-gate 				dprintf(("! %d: terminated, status 0x%.4x\n", \
451*0Sstevel@tonic-gate 				(int)pidtable[i].pl_pid, psinfo.pr_wstat));
452*0Sstevel@tonic-gate 				pidtable[i].pl_status = psinfo.pr_wstat;
453*0Sstevel@tonic-gate 
454*0Sstevel@tonic-gate 			} else {
455*0Sstevel@tonic-gate 				dprintf(("! %d: terminated\n", \
456*0Sstevel@tonic-gate 						(int)pidtable[i].pl_pid));
457*0Sstevel@tonic-gate 				pidtable[i].pl_status = 0;
458*0Sstevel@tonic-gate 			}
459*0Sstevel@tonic-gate 			/*
460*0Sstevel@tonic-gate 			 * PID gets removed when terminated only
461*0Sstevel@tonic-gate 			 */
462*0Sstevel@tonic-gate 			rem_pid((pid_t)0, i, CLEANIT);
463*0Sstevel@tonic-gate 			ret_val--;
464*0Sstevel@tonic-gate 			continue;
465*0Sstevel@tonic-gate 		}
466*0Sstevel@tonic-gate 		/*
467*0Sstevel@tonic-gate 		 * POLLNVAL and POLLERR
468*0Sstevel@tonic-gate 		 *	These error's shouldn't occurr but until their fixed
469*0Sstevel@tonic-gate 		 *	we perform some simple error recovery.
470*0Sstevel@tonic-gate 		 */
471*0Sstevel@tonic-gate 		if (pfd->revents & (POLLNVAL|POLLERR)) {
472*0Sstevel@tonic-gate 			dprintf(("Poll Err = %d pid = %d i = %d\n", \
473*0Sstevel@tonic-gate 				pfd->revents, \
474*0Sstevel@tonic-gate 				(int)pidtable[i].pl_pid, i));
475*0Sstevel@tonic-gate 
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate 			pid = pidtable[i].pl_pid; /* Save pid for below */
478*0Sstevel@tonic-gate 			/*
479*0Sstevel@tonic-gate 			 * If its POLLNVAL we just remove the process for
480*0Sstevel@tonic-gate 			 * now, it will get picked up in the next scan.
481*0Sstevel@tonic-gate 			 * POLLERR pids get re-added after being deleted.
482*0Sstevel@tonic-gate 			 */
483*0Sstevel@tonic-gate 			if (pfd->revents & POLLNVAL) {
484*0Sstevel@tonic-gate 				rem_pid((pid_t)0, i, DONT_CLEAN);
485*0Sstevel@tonic-gate 			} else {			/* Else... POLLERR */
486*0Sstevel@tonic-gate 				rem_pid((pid_t)0, i, DONT_CLEAN);
487*0Sstevel@tonic-gate 				add_pid(pid);
488*0Sstevel@tonic-gate 			}
489*0Sstevel@tonic-gate 
490*0Sstevel@tonic-gate 			if (bad_error++ > MAX_POLL_ERRS) {
491*0Sstevel@tonic-gate 				bad_error = 0;
492*0Sstevel@tonic-gate 				return (0);	/* 0 Indicates severe error */
493*0Sstevel@tonic-gate 			}
494*0Sstevel@tonic-gate 			ret_val--;
495*0Sstevel@tonic-gate 			continue;
496*0Sstevel@tonic-gate 		}
497*0Sstevel@tonic-gate 
498*0Sstevel@tonic-gate 		/*
499*0Sstevel@tonic-gate 		 * No more bits should be set in revents but check anyway
500*0Sstevel@tonic-gate 		 */
501*0Sstevel@tonic-gate 		if (pfd->revents != 0) {
502*0Sstevel@tonic-gate 			dprintf(("%d: unknown err %d\n", \
503*0Sstevel@tonic-gate 			    (int)pidtable[i].pl_pid, pfd->revents));
504*0Sstevel@tonic-gate 
505*0Sstevel@tonic-gate 			rem_pid((pid_t)0, i, DONT_CLEAN);
506*0Sstevel@tonic-gate 			ret_val--;
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate 			if (bad_error++ > MAX_POLL_ERRS) {
509*0Sstevel@tonic-gate 				bad_error = 0;
510*0Sstevel@tonic-gate 				return (0);	/* 0 Indicates severe error */
511*0Sstevel@tonic-gate 			}
512*0Sstevel@tonic-gate 			return (1);
513*0Sstevel@tonic-gate 		}
514*0Sstevel@tonic-gate 	}
515*0Sstevel@tonic-gate 	return (1);			/* 1 Indicates Everything okay */
516*0Sstevel@tonic-gate }
517*0Sstevel@tonic-gate 
518*0Sstevel@tonic-gate /*
519*0Sstevel@tonic-gate  *		*** The Scanner ***
520*0Sstevel@tonic-gate  *
521*0Sstevel@tonic-gate  * scan_utmps()		- Scan the utmpx file.
522*0Sstevel@tonic-gate  *			  For each USER_PROCESS check
523*0Sstevel@tonic-gate  *			  if its alive or dead.  If alive and its not in
524*0Sstevel@tonic-gate  *			  our table to be watched, put it there.  If its
525*0Sstevel@tonic-gate  *			  dead, remove it from our table and clean it up.
526*0Sstevel@tonic-gate  */
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate static void
529*0Sstevel@tonic-gate scan_utmps()
530*0Sstevel@tonic-gate {
531*0Sstevel@tonic-gate 	struct	utmpx	*utmpx;
532*0Sstevel@tonic-gate 	int	i;
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate 	dprintf(("Scan utmps\n"));
535*0Sstevel@tonic-gate 	/*
536*0Sstevel@tonic-gate 	 * Scan utmpx.
537*0Sstevel@tonic-gate 	 */
538*0Sstevel@tonic-gate 	setutxent();
539*0Sstevel@tonic-gate 	while ((utmpx = getutxent()) != NULL) {
540*0Sstevel@tonic-gate 		if (utmpx->ut_type == USER_PROCESS) {
541*0Sstevel@tonic-gate 			/*
542*0Sstevel@tonic-gate 			 * Is the process alive?
543*0Sstevel@tonic-gate 			 */
544*0Sstevel@tonic-gate 			if (proc_is_alive(utmpx->ut_pid)) {
545*0Sstevel@tonic-gate 				/*
546*0Sstevel@tonic-gate 				 * Yes, the process is alive, so add it if we
547*0Sstevel@tonic-gate 				 * don't have it in our table.
548*0Sstevel@tonic-gate 				 */
549*0Sstevel@tonic-gate 				if (find_pid(utmpx->ut_pid, &i) == 0)
550*0Sstevel@tonic-gate 					add_pid(utmpx->ut_pid);	/* No, add it */
551*0Sstevel@tonic-gate 			} else {
552*0Sstevel@tonic-gate 				/*
553*0Sstevel@tonic-gate 				 * No, the process is dead, so remove it if its
554*0Sstevel@tonic-gate 				 * in our table, otherwise just clean it.
555*0Sstevel@tonic-gate 				 */
556*0Sstevel@tonic-gate 				if (find_pid(utmpx->ut_pid, &i) == 1)
557*0Sstevel@tonic-gate 					rem_pid(utmpx->ut_pid, i, CLEANIT);
558*0Sstevel@tonic-gate 				else
559*0Sstevel@tonic-gate 					clean_utmpx_ent(utmpx);
560*0Sstevel@tonic-gate 			}
561*0Sstevel@tonic-gate 		}
562*0Sstevel@tonic-gate 	}
563*0Sstevel@tonic-gate 	/*
564*0Sstevel@tonic-gate 	 * Close it to flush the buffer.
565*0Sstevel@tonic-gate 	 */
566*0Sstevel@tonic-gate 	endutxent();
567*0Sstevel@tonic-gate }
568*0Sstevel@tonic-gate 
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate /*
571*0Sstevel@tonic-gate  *			*** Receiver Routines ***
572*0Sstevel@tonic-gate  */
573*0Sstevel@tonic-gate 
574*0Sstevel@tonic-gate /*
575*0Sstevel@tonic-gate  * setup_pipe	- Set up the pipe to read pids over
576*0Sstevel@tonic-gate  */
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate static void
579*0Sstevel@tonic-gate setup_pipe()
580*0Sstevel@tonic-gate {
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate 	struct statvfs statvfs_buf;
583*0Sstevel@tonic-gate 	/*
584*0Sstevel@tonic-gate 	 * This code & comments swiped from init and left stock since it works
585*0Sstevel@tonic-gate 	 */
586*0Sstevel@tonic-gate 
587*0Sstevel@tonic-gate 	if (Pfd < 0) {
588*0Sstevel@tonic-gate 		if ((statvfs(UTMPPIPE_DIR, &statvfs_buf) == 0) &&
589*0Sstevel@tonic-gate 		    ((statvfs_buf.f_flag & ST_RDONLY) == 0)) {
590*0Sstevel@tonic-gate 			(void) unlink(UTMPPIPE);
591*0Sstevel@tonic-gate 			(void) mknod(UTMPPIPE, S_IFIFO | 0600, 0);
592*0Sstevel@tonic-gate 		}
593*0Sstevel@tonic-gate 		Pfd = open(UTMPPIPE, O_RDWR | O_NDELAY);
594*0Sstevel@tonic-gate 	}
595*0Sstevel@tonic-gate 	if (Pfd < 0)
596*0Sstevel@tonic-gate 		nonfatal(UTMPPIPE);
597*0Sstevel@tonic-gate 	/*
598*0Sstevel@tonic-gate 	 * This code from init modified to be poll based instead of SIGPOLL,
599*0Sstevel@tonic-gate 	 * signal based.
600*0Sstevel@tonic-gate 	 */
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 	if (Pfd >= 0) {
603*0Sstevel@tonic-gate 		/*
604*0Sstevel@tonic-gate 		 * Read pipe in message discard mode.  When read reads a
605*0Sstevel@tonic-gate 		 * pidrec size record, the remainder of the message will
606*0Sstevel@tonic-gate 		 * be discarded.  Though there shouldn't be any it will
607*0Sstevel@tonic-gate 		 * help resynch if someone else wrote some garbage.
608*0Sstevel@tonic-gate 		 */
609*0Sstevel@tonic-gate 		(void) ioctl(Pfd, I_SRDOPT, RMSGD);
610*0Sstevel@tonic-gate 	}
611*0Sstevel@tonic-gate 
612*0Sstevel@tonic-gate 	/*
613*0Sstevel@tonic-gate 	 * My code.  We use slot 0 in the table to hold the fd of the pipe
614*0Sstevel@tonic-gate 	 */
615*0Sstevel@tonic-gate 	add_pid(0);			/* Proc 0 guaranteed to get slot 0 */
616*0Sstevel@tonic-gate 	fdtable[0].fd = Pfd;		/* Pfd could be -1, should be okay */
617*0Sstevel@tonic-gate 	fdtable[0].events = POLLRDNORM;
618*0Sstevel@tonic-gate }
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate /*
621*0Sstevel@tonic-gate  * drain_pipe()		- The receiver routine that reads the pipe
622*0Sstevel@tonic-gate  */
623*0Sstevel@tonic-gate 
624*0Sstevel@tonic-gate static void
625*0Sstevel@tonic-gate drain_pipe()
626*0Sstevel@tonic-gate {
627*0Sstevel@tonic-gate 	struct pidrec prec;
628*0Sstevel@tonic-gate 	register struct pidrec *p = &prec;
629*0Sstevel@tonic-gate 	int bytes_read;
630*0Sstevel@tonic-gate 	int i;
631*0Sstevel@tonic-gate 
632*0Sstevel@tonic-gate 	for (;;) {
633*0Sstevel@tonic-gate 		/*
634*0Sstevel@tonic-gate 		 * Important Note: Either read will really fail (in which case
635*0Sstevel@tonic-gate 		 * return is all we can do) or will get EAGAIN (Pfd was opened
636*0Sstevel@tonic-gate 		 * O_NDELAY), in which case we also want to return.
637*0Sstevel@tonic-gate 		 */
638*0Sstevel@tonic-gate 
639*0Sstevel@tonic-gate 		if ((bytes_read = read(Pfd, p, sizeof (struct pidrec))) !=
640*0Sstevel@tonic-gate 		    sizeof (struct pidrec))  {
641*0Sstevel@tonic-gate 			/*
642*0Sstevel@tonic-gate 			 * Something went wrong reading, so read until pipe
643*0Sstevel@tonic-gate 			 * is empty
644*0Sstevel@tonic-gate 			 */
645*0Sstevel@tonic-gate 			if (bytes_read > 0)
646*0Sstevel@tonic-gate 				while (read(Pfd, p, sizeof (struct pidrec)) > 0)
647*0Sstevel@tonic-gate 					;
648*0Sstevel@tonic-gate 			return;
649*0Sstevel@tonic-gate 		}
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate 		dprintf(("drain_pipe: Recd command %d, pid %d\n",
652*0Sstevel@tonic-gate 			p->pd_type, (int)p->pd_pid));
653*0Sstevel@tonic-gate 		switch (p->pd_type) {
654*0Sstevel@tonic-gate 		case ADDPID:
655*0Sstevel@tonic-gate 			/*
656*0Sstevel@tonic-gate 			 * Check if we already have the process, adding it
657*0Sstevel@tonic-gate 			 * if we don't.
658*0Sstevel@tonic-gate 			 */
659*0Sstevel@tonic-gate 			if (find_pid(p->pd_pid, &i) == 0)
660*0Sstevel@tonic-gate 				add_pid(p->pd_pid);
661*0Sstevel@tonic-gate 			break;
662*0Sstevel@tonic-gate 
663*0Sstevel@tonic-gate 		case REMPID:
664*0Sstevel@tonic-gate 			rem_pid(p->pd_pid, -1, DONT_CLEAN);
665*0Sstevel@tonic-gate 			break;
666*0Sstevel@tonic-gate 		default:
667*0Sstevel@tonic-gate 			nonfatal("Bad message on utmppipe\n");
668*0Sstevel@tonic-gate 				break;
669*0Sstevel@tonic-gate 		}
670*0Sstevel@tonic-gate 	}
671*0Sstevel@tonic-gate }
672*0Sstevel@tonic-gate 
673*0Sstevel@tonic-gate 
674*0Sstevel@tonic-gate /*
675*0Sstevel@tonic-gate  *		*** Utilities for add and removing entries in the tables ***
676*0Sstevel@tonic-gate  */
677*0Sstevel@tonic-gate 
678*0Sstevel@tonic-gate /*
679*0Sstevel@tonic-gate  * add_pid	- add a pid to the fd table and the pidtable.
680*0Sstevel@tonic-gate  *		  these tables are sorted tables for quick lookups.
681*0Sstevel@tonic-gate  *
682*0Sstevel@tonic-gate  */
683*0Sstevel@tonic-gate static void
684*0Sstevel@tonic-gate add_pid(pid)
685*0Sstevel@tonic-gate 	pid_t pid;
686*0Sstevel@tonic-gate {
687*0Sstevel@tonic-gate 	int fd = 0;
688*0Sstevel@tonic-gate 	int i = 0, move_amt;
689*0Sstevel@tonic-gate 	int j;
690*0Sstevel@tonic-gate 	static int first_time = 1;
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 	/*
693*0Sstevel@tonic-gate 	 * Check to see if the pid is already in our table, or being passed
694*0Sstevel@tonic-gate 	 * pid zero.
695*0Sstevel@tonic-gate 	 */
696*0Sstevel@tonic-gate 	if (pidcnt != 0 && (find_pid(pid, &j) == 1 || pid == 0))
697*0Sstevel@tonic-gate 		return;
698*0Sstevel@tonic-gate 
699*0Sstevel@tonic-gate 	if (pidcnt >= Max_fds) {
700*0Sstevel@tonic-gate 		if (first_time == 1) {
701*0Sstevel@tonic-gate 			/*
702*0Sstevel@tonic-gate 			 * Print this error only once
703*0Sstevel@tonic-gate 			 */
704*0Sstevel@tonic-gate 			nonfatal("File Descriptor limit exceeded");
705*0Sstevel@tonic-gate 			first_time = 0;
706*0Sstevel@tonic-gate 		}
707*0Sstevel@tonic-gate 		return;
708*0Sstevel@tonic-gate 	}
709*0Sstevel@tonic-gate 	/*
710*0Sstevel@tonic-gate 	 * Open the /proc file checking if there's still a valid proc file.
711*0Sstevel@tonic-gate 	 */
712*0Sstevel@tonic-gate 	if (pid != 0 && (fd = proc_to_fd(pid)) == -1) {
713*0Sstevel@tonic-gate 		/*
714*0Sstevel@tonic-gate 		 * No so the process died before we got to watch for him
715*0Sstevel@tonic-gate 		 */
716*0Sstevel@tonic-gate 		return;
717*0Sstevel@tonic-gate 	}
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate 	/*
720*0Sstevel@tonic-gate 	 * We only do this code if we're not putting in the first element
721*0Sstevel@tonic-gate 	 * Which we know will be for proc zero which is used by setup_pipe
722*0Sstevel@tonic-gate 	 * for its pipe fd.
723*0Sstevel@tonic-gate 	 */
724*0Sstevel@tonic-gate 	if (pidcnt != 0) {
725*0Sstevel@tonic-gate 		for (i = 0; i < pidcnt; i++) {
726*0Sstevel@tonic-gate 			if (pid <= pidtable[i].pl_pid)
727*0Sstevel@tonic-gate 				break;
728*0Sstevel@tonic-gate 		}
729*0Sstevel@tonic-gate 
730*0Sstevel@tonic-gate 		/*
731*0Sstevel@tonic-gate 		 * Handle the case where we're not sticking our entry on the
732*0Sstevel@tonic-gate 		 * the end, or overwriting an existing entry.
733*0Sstevel@tonic-gate 		 */
734*0Sstevel@tonic-gate 		if (i != pidcnt && pid != pidtable[i].pl_pid) {
735*0Sstevel@tonic-gate 
736*0Sstevel@tonic-gate 			move_amt = pidcnt - i;
737*0Sstevel@tonic-gate 			/*
738*0Sstevel@tonic-gate 			 * Move table down
739*0Sstevel@tonic-gate 			 */
740*0Sstevel@tonic-gate 			if (move_amt != 0) {
741*0Sstevel@tonic-gate 				(void) memmove(&pidtable[i+1], &pidtable[i],
742*0Sstevel@tonic-gate 					move_amt * sizeof (struct pidentry));
743*0Sstevel@tonic-gate 				(void) memmove(&fdtable[i+1], &fdtable[i],
744*0Sstevel@tonic-gate 					move_amt * sizeof (pollfd_t));
745*0Sstevel@tonic-gate 			}
746*0Sstevel@tonic-gate 		}
747*0Sstevel@tonic-gate 	}
748*0Sstevel@tonic-gate 
749*0Sstevel@tonic-gate 	/*
750*0Sstevel@tonic-gate 	 * Fill in the events field for poll and copy the entry into the array
751*0Sstevel@tonic-gate 	 */
752*0Sstevel@tonic-gate 	fdtable[i].events = 0;
753*0Sstevel@tonic-gate 	fdtable[i].revents = 0;
754*0Sstevel@tonic-gate 	fdtable[i].fd = fd;
755*0Sstevel@tonic-gate 
756*0Sstevel@tonic-gate 	/*
757*0Sstevel@tonic-gate 	 * Likewise, setup pid field and pointer (index) to the fdtable entry
758*0Sstevel@tonic-gate 	 */
759*0Sstevel@tonic-gate 	pidtable[i].pl_pid = pid;
760*0Sstevel@tonic-gate 
761*0Sstevel@tonic-gate 	pidcnt++;			/* Bump the pid count */
762*0Sstevel@tonic-gate 	dprintf(("  add_pid: pid = %d fd = %d index = %d pidcnt = %d\n",
763*0Sstevel@tonic-gate 		(int)pid, fd, i, pidcnt));
764*0Sstevel@tonic-gate }
765*0Sstevel@tonic-gate 
766*0Sstevel@tonic-gate 
767*0Sstevel@tonic-gate /*
768*0Sstevel@tonic-gate  * rem_pid	- Remove an entry from the table and check to see if its
769*0Sstevel@tonic-gate  *		  not in the utmpx file.
770*0Sstevel@tonic-gate  *		  If i != -1 don't look up the pid, use i as index
771*0Sstevel@tonic-gate  */
772*0Sstevel@tonic-gate 
773*0Sstevel@tonic-gate static void
774*0Sstevel@tonic-gate rem_pid(pid, i, clean_it)
775*0Sstevel@tonic-gate 	pid_t pid;	/* Pid of process to clean or 0 if we don't know it */
776*0Sstevel@tonic-gate 	int i;		/* Index into table or -1 if we need to look it up */
777*0Sstevel@tonic-gate 	int clean_it;	/* Clean the entry, or just remove from table? */
778*0Sstevel@tonic-gate {
779*0Sstevel@tonic-gate 	int move_amt;
780*0Sstevel@tonic-gate 
781*0Sstevel@tonic-gate 	dprintf(("  rem_pid: pid = %d i = %d", (int)pid, i));
782*0Sstevel@tonic-gate 
783*0Sstevel@tonic-gate 	/*
784*0Sstevel@tonic-gate 	 * Don't allow slot 0 in the table to be removed - utmppipe fd
785*0Sstevel@tonic-gate 	 */
786*0Sstevel@tonic-gate 	if ((i == -1 && pid == 0) || (i == 0))	{
787*0Sstevel@tonic-gate 		dprintf((" - attempted to remove proc 0\n"));
788*0Sstevel@tonic-gate 		return;
789*0Sstevel@tonic-gate 	}
790*0Sstevel@tonic-gate 
791*0Sstevel@tonic-gate 	if (i != -1 || find_pid(pid, &i) == 1) {	/* Found the entry */
792*0Sstevel@tonic-gate 		(void) close(fdtable[i].fd);	/* We're done with the fd */
793*0Sstevel@tonic-gate 
794*0Sstevel@tonic-gate 		dprintf((" fd = %d\n", fdtable[i].fd));
795*0Sstevel@tonic-gate 
796*0Sstevel@tonic-gate 		if (clean_it == CLEANIT)
797*0Sstevel@tonic-gate 			clean_entry(i);
798*0Sstevel@tonic-gate 
799*0Sstevel@tonic-gate 		move_amt = (pidcnt - i) - 1;
800*0Sstevel@tonic-gate 		/*
801*0Sstevel@tonic-gate 		 * Remove entries from the tables.
802*0Sstevel@tonic-gate 		 */
803*0Sstevel@tonic-gate 		(void) memmove(&pidtable[i], &pidtable[i+1],
804*0Sstevel@tonic-gate 			move_amt * sizeof (struct pidentry));
805*0Sstevel@tonic-gate 
806*0Sstevel@tonic-gate 		(void) memmove(&fdtable[i], &fdtable[i+1],
807*0Sstevel@tonic-gate 			move_amt * sizeof (pollfd_t));
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate 		/*
810*0Sstevel@tonic-gate 		 * decrement the pid count - one less pid to worry about
811*0Sstevel@tonic-gate 		 */
812*0Sstevel@tonic-gate 		pidcnt--;
813*0Sstevel@tonic-gate 	}
814*0Sstevel@tonic-gate 	if (i == -1)
815*0Sstevel@tonic-gate 		dprintf((" - entry not found \n"));
816*0Sstevel@tonic-gate }
817*0Sstevel@tonic-gate 
818*0Sstevel@tonic-gate 
819*0Sstevel@tonic-gate /*
820*0Sstevel@tonic-gate  * find_pid	- Returns an index into the pidtable of the specifed pid,
821*0Sstevel@tonic-gate  *		  else -1 if not found
822*0Sstevel@tonic-gate  */
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate static int
825*0Sstevel@tonic-gate find_pid(pid, i)
826*0Sstevel@tonic-gate 	pid_t pid;
827*0Sstevel@tonic-gate 	int *i;
828*0Sstevel@tonic-gate {
829*0Sstevel@tonic-gate 	struct pidentry pe;
830*0Sstevel@tonic-gate 	struct pidentry *p;
831*0Sstevel@tonic-gate 
832*0Sstevel@tonic-gate 	pe.pl_pid = pid;
833*0Sstevel@tonic-gate 	p = bsearch(&pe, pidtable, pidcnt, sizeof (struct pidentry), pidcmp);
834*0Sstevel@tonic-gate 
835*0Sstevel@tonic-gate 	if (p == NULL)
836*0Sstevel@tonic-gate 		return (0);
837*0Sstevel@tonic-gate 	else {
838*0Sstevel@tonic-gate 		*i = p - (struct pidentry *)pidtable;
839*0Sstevel@tonic-gate 		return (1);
840*0Sstevel@tonic-gate 	}
841*0Sstevel@tonic-gate }
842*0Sstevel@tonic-gate 
843*0Sstevel@tonic-gate 
844*0Sstevel@tonic-gate /*
845*0Sstevel@tonic-gate  * Pidcmp - Used by besearch for sorting and finding  process IDs.
846*0Sstevel@tonic-gate  */
847*0Sstevel@tonic-gate 
848*0Sstevel@tonic-gate static int
849*0Sstevel@tonic-gate pidcmp(a, b)
850*0Sstevel@tonic-gate 	struct pidentry *a, *b;
851*0Sstevel@tonic-gate {
852*0Sstevel@tonic-gate 	if (b == NULL || a == NULL)
853*0Sstevel@tonic-gate 		return (0);
854*0Sstevel@tonic-gate 	return (a->pl_pid - b->pl_pid);
855*0Sstevel@tonic-gate }
856*0Sstevel@tonic-gate 
857*0Sstevel@tonic-gate 
858*0Sstevel@tonic-gate /*
859*0Sstevel@tonic-gate  * proc_to_fd	- Take a process ID and return an open file descriptor to the
860*0Sstevel@tonic-gate  *		  /proc file for the specified process.
861*0Sstevel@tonic-gate  */
862*0Sstevel@tonic-gate static int
863*0Sstevel@tonic-gate proc_to_fd(pid)
864*0Sstevel@tonic-gate 	pid_t pid;
865*0Sstevel@tonic-gate {
866*0Sstevel@tonic-gate 	char procname[64];
867*0Sstevel@tonic-gate 	int fd, dfd;
868*0Sstevel@tonic-gate 
869*0Sstevel@tonic-gate 	(void) sprintf(procname, "/proc/%d/psinfo", (int)pid);
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate 	if ((fd = open(procname, O_RDONLY)) >= 0) {
872*0Sstevel@tonic-gate 		/*
873*0Sstevel@tonic-gate 		 * dup the fd above the low order values to assure
874*0Sstevel@tonic-gate 		 * stdio works for other fds - paranoia.
875*0Sstevel@tonic-gate 		 */
876*0Sstevel@tonic-gate 		if (fd < EXTRA_MARGIN) {
877*0Sstevel@tonic-gate 			dfd = fcntl(fd, F_DUPFD, EXTRA_MARGIN);
878*0Sstevel@tonic-gate 			if (dfd > 0) {
879*0Sstevel@tonic-gate 				(void) close(fd);
880*0Sstevel@tonic-gate 				fd = dfd;
881*0Sstevel@tonic-gate 			}
882*0Sstevel@tonic-gate 		}
883*0Sstevel@tonic-gate 		/*
884*0Sstevel@tonic-gate 		 * More paranoia - set the close on exec flag
885*0Sstevel@tonic-gate 		 */
886*0Sstevel@tonic-gate 		(void) fcntl(fd, F_SETFD, 1);
887*0Sstevel@tonic-gate 		return (fd);
888*0Sstevel@tonic-gate 	}
889*0Sstevel@tonic-gate 	if (errno == ENOENT)
890*0Sstevel@tonic-gate 		return (-1);
891*0Sstevel@tonic-gate 
892*0Sstevel@tonic-gate 	if (errno == EMFILE) {
893*0Sstevel@tonic-gate 		/*
894*0Sstevel@tonic-gate 		 * This is fatal, since libc won't be able to allocate
895*0Sstevel@tonic-gate 		 * any fds for the pututxline() routines
896*0Sstevel@tonic-gate 		 */
897*0Sstevel@tonic-gate 		fatal("Out of file descriptors");
898*0Sstevel@tonic-gate 	}
899*0Sstevel@tonic-gate 	fatal(procname);		/* Only get here on error */
900*0Sstevel@tonic-gate 	return (-1);
901*0Sstevel@tonic-gate }
902*0Sstevel@tonic-gate 
903*0Sstevel@tonic-gate 
904*0Sstevel@tonic-gate /*
905*0Sstevel@tonic-gate  *		*** Utmpx Cleaning Utilities ***
906*0Sstevel@tonic-gate  */
907*0Sstevel@tonic-gate 
908*0Sstevel@tonic-gate /*
909*0Sstevel@tonic-gate  * Clean_entry	- Cleans the specified entry - where i is an index
910*0Sstevel@tonic-gate  *		  into the pid_table.
911*0Sstevel@tonic-gate  */
912*0Sstevel@tonic-gate static void
913*0Sstevel@tonic-gate clean_entry(i)
914*0Sstevel@tonic-gate 	int i;
915*0Sstevel@tonic-gate {
916*0Sstevel@tonic-gate 	struct utmpx *u;
917*0Sstevel@tonic-gate 
918*0Sstevel@tonic-gate 	if (pidcnt == 0)
919*0Sstevel@tonic-gate 		return;
920*0Sstevel@tonic-gate 
921*0Sstevel@tonic-gate 	dprintf(("    Cleaning %d\n", (int)pidtable[i].pl_pid));
922*0Sstevel@tonic-gate 
923*0Sstevel@tonic-gate 	/*
924*0Sstevel@tonic-gate 	 * Double check if the process is dead.
925*0Sstevel@tonic-gate 	 */
926*0Sstevel@tonic-gate 	if (proc_is_alive(pidtable[i].pl_pid)) {
927*0Sstevel@tonic-gate 		dprintf(("      Bad attempt to clean %d\n", \
928*0Sstevel@tonic-gate 			(int)pidtable[i].pl_pid));
929*0Sstevel@tonic-gate 		return;
930*0Sstevel@tonic-gate 	}
931*0Sstevel@tonic-gate 
932*0Sstevel@tonic-gate 	/*
933*0Sstevel@tonic-gate 	 * Find the entry that corresponds to this pid.
934*0Sstevel@tonic-gate 	 * Do nothing if entry not found in utmpx file.
935*0Sstevel@tonic-gate 	 */
936*0Sstevel@tonic-gate 	setutxent();
937*0Sstevel@tonic-gate 	while ((u = getutxent()) != NULL) {
938*0Sstevel@tonic-gate 		if (u->ut_pid == pidtable[i].pl_pid) {
939*0Sstevel@tonic-gate 			if (u->ut_type == USER_PROCESS) {
940*0Sstevel@tonic-gate 				clean_utmpx_ent(u);
941*0Sstevel@tonic-gate 			}
942*0Sstevel@tonic-gate 		}
943*0Sstevel@tonic-gate 	}
944*0Sstevel@tonic-gate 	endutxent();
945*0Sstevel@tonic-gate }
946*0Sstevel@tonic-gate 
947*0Sstevel@tonic-gate 
948*0Sstevel@tonic-gate /*
949*0Sstevel@tonic-gate  * clean_utmpx_ent	- Clean a utmpx entry
950*0Sstevel@tonic-gate  */
951*0Sstevel@tonic-gate 
952*0Sstevel@tonic-gate static void
953*0Sstevel@tonic-gate clean_utmpx_ent(u)
954*0Sstevel@tonic-gate 	struct utmpx *u;
955*0Sstevel@tonic-gate {
956*0Sstevel@tonic-gate 	dprintf(("      clean_utmpx_ent: %d\n", (int)u->ut_pid));
957*0Sstevel@tonic-gate 	u->ut_type = DEAD_PROCESS;
958*0Sstevel@tonic-gate 	(void) time(&u->ut_xtime);
959*0Sstevel@tonic-gate 	(void) pututxline(u);
960*0Sstevel@tonic-gate 	updwtmpx(WTMPX_FILE, u);
961*0Sstevel@tonic-gate 	/*
962*0Sstevel@tonic-gate 	 * XXX update wtmp for ! nonuser entries?
963*0Sstevel@tonic-gate 	 */
964*0Sstevel@tonic-gate }
965*0Sstevel@tonic-gate 
966*0Sstevel@tonic-gate /*
967*0Sstevel@tonic-gate  *		*** Error Handling and Debugging Routines ***
968*0Sstevel@tonic-gate  */
969*0Sstevel@tonic-gate 
970*0Sstevel@tonic-gate /*
971*0Sstevel@tonic-gate  * fatal - Catastrophic failure
972*0Sstevel@tonic-gate  */
973*0Sstevel@tonic-gate 
974*0Sstevel@tonic-gate static void
975*0Sstevel@tonic-gate fatal(char *str)
976*0Sstevel@tonic-gate {
977*0Sstevel@tonic-gate 	int oerrno = errno;
978*0Sstevel@tonic-gate 
979*0Sstevel@tonic-gate 	syslog(LOG_ALERT, str);
980*0Sstevel@tonic-gate 	if (Debug == 1) {
981*0Sstevel@tonic-gate 		if ((errno = oerrno) != 0)
982*0Sstevel@tonic-gate 			perror(prog_name);
983*0Sstevel@tonic-gate 		dprintf(("%s\n", str));
984*0Sstevel@tonic-gate 	}
985*0Sstevel@tonic-gate 	exit(-1);
986*0Sstevel@tonic-gate }
987*0Sstevel@tonic-gate 
988*0Sstevel@tonic-gate /*
989*0Sstevel@tonic-gate  * nonfatal - Non-Catastrophic failure - print message and errno
990*0Sstevel@tonic-gate  */
991*0Sstevel@tonic-gate 
992*0Sstevel@tonic-gate static void
993*0Sstevel@tonic-gate nonfatal(char *str)
994*0Sstevel@tonic-gate {
995*0Sstevel@tonic-gate 	syslog(LOG_WARNING, str);
996*0Sstevel@tonic-gate 
997*0Sstevel@tonic-gate 	if (Debug == 1) {
998*0Sstevel@tonic-gate 		if (errno != 0)
999*0Sstevel@tonic-gate 			perror(prog_name);
1000*0Sstevel@tonic-gate 		dprintf(("%c%s\n", 7, str));
1001*0Sstevel@tonic-gate 		print_tables();
1002*0Sstevel@tonic-gate 		(void) sleep(5);	/* Time to read debug messages */
1003*0Sstevel@tonic-gate 	}
1004*0Sstevel@tonic-gate }
1005*0Sstevel@tonic-gate 
1006*0Sstevel@tonic-gate /*
1007*0Sstevel@tonic-gate  * print_tables	- Print internal tables - for debugging
1008*0Sstevel@tonic-gate  */
1009*0Sstevel@tonic-gate 
1010*0Sstevel@tonic-gate static void
1011*0Sstevel@tonic-gate print_tables()
1012*0Sstevel@tonic-gate {
1013*0Sstevel@tonic-gate 	int i;
1014*0Sstevel@tonic-gate 
1015*0Sstevel@tonic-gate 	if (Debug == 0)
1016*0Sstevel@tonic-gate 		return;
1017*0Sstevel@tonic-gate 
1018*0Sstevel@tonic-gate 	dprintf(("pidtable: "));
1019*0Sstevel@tonic-gate 	for (i = 0; i < pidcnt; i++)
1020*0Sstevel@tonic-gate 		dprintf(("%d: %d  ", i, (int)pidtable[i].pl_pid));
1021*0Sstevel@tonic-gate 	dprintf(("\n"));
1022*0Sstevel@tonic-gate 	dprintf(("fdtable:  "));
1023*0Sstevel@tonic-gate 	for (i = 0; i < pidcnt; i++)
1024*0Sstevel@tonic-gate 		dprintf(("%d: %d  ", i, fdtable[i].fd));
1025*0Sstevel@tonic-gate 	dprintf(("\n"));
1026*0Sstevel@tonic-gate }
1027*0Sstevel@tonic-gate 
1028*0Sstevel@tonic-gate /*
1029*0Sstevel@tonic-gate  * proc_is_alive	- Check to see if a process is alive AND its
1030*0Sstevel@tonic-gate  *			  not a zombie.  Returns 1 if process is alive
1031*0Sstevel@tonic-gate  *			  and zero if it is dead or a zombie.
1032*0Sstevel@tonic-gate  */
1033*0Sstevel@tonic-gate 
1034*0Sstevel@tonic-gate static int
1035*0Sstevel@tonic-gate proc_is_alive(pid)
1036*0Sstevel@tonic-gate 	pid_t pid;
1037*0Sstevel@tonic-gate {
1038*0Sstevel@tonic-gate 	char psinfoname[64];
1039*0Sstevel@tonic-gate 	int fd;
1040*0Sstevel@tonic-gate 	psinfo_t psinfo;
1041*0Sstevel@tonic-gate 
1042*0Sstevel@tonic-gate 	if (kill(pid, 0) != 0)
1043*0Sstevel@tonic-gate 		return (0);		/* Kill failed - no process */
1044*0Sstevel@tonic-gate 
1045*0Sstevel@tonic-gate 	/*
1046*0Sstevel@tonic-gate 	 * The process exists, so check if it's a zombie.
1047*0Sstevel@tonic-gate 	 */
1048*0Sstevel@tonic-gate 	(void) sprintf(psinfoname, "/proc/%d/psinfo", (int)pid);
1049*0Sstevel@tonic-gate 
1050*0Sstevel@tonic-gate 	if ((fd = open(psinfoname, O_RDONLY)) < 0 ||
1051*0Sstevel@tonic-gate 	    read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
1052*0Sstevel@tonic-gate 		/*
1053*0Sstevel@tonic-gate 		 * We either couldn't open the proc, or we did but the
1054*0Sstevel@tonic-gate 		 * read of the psinfo file failed, so pid is nonexistent.
1055*0Sstevel@tonic-gate 		 */
1056*0Sstevel@tonic-gate 		psinfo.pr_nlwp = 0;
1057*0Sstevel@tonic-gate 	}
1058*0Sstevel@tonic-gate 	if (fd >= 0)
1059*0Sstevel@tonic-gate 		(void) close(fd);
1060*0Sstevel@tonic-gate 
1061*0Sstevel@tonic-gate 	/* if pr_nlwp == 0, process is a zombie */
1062*0Sstevel@tonic-gate 	return (psinfo.pr_nlwp != 0);
1063*0Sstevel@tonic-gate }
1064*0Sstevel@tonic-gate 
1065*0Sstevel@tonic-gate /*
1066*0Sstevel@tonic-gate  * warn_utmp -	/var/adm/utmp has been deprecated. It should no longer
1067*0Sstevel@tonic-gate  *		be used.  Applications that try to directly manipulate
1068*0Sstevel@tonic-gate  *		it may cause problems. Since the file is no longer
1069*0Sstevel@tonic-gate  *		shipped, if it appears on a system it's because an
1070*0Sstevel@tonic-gate  *		old application created it.  We'll have utmpd
1071*0Sstevel@tonic-gate  *		complain about it periodically.
1072*0Sstevel@tonic-gate  */
1073*0Sstevel@tonic-gate 
1074*0Sstevel@tonic-gate static void
1075*0Sstevel@tonic-gate warn_utmp()
1076*0Sstevel@tonic-gate {
1077*0Sstevel@tonic-gate 	struct stat s;
1078*0Sstevel@tonic-gate 
1079*0Sstevel@tonic-gate 	if (lstat(UTMP_FILE, &s) == 0 &&
1080*0Sstevel@tonic-gate 	    s.st_size % sizeof (struct utmp) == 0) {
1081*0Sstevel@tonic-gate 		nonfatal("WARNING: /var/adm/utmp exists!\nSee "
1082*0Sstevel@tonic-gate 		    "utmp(4) for more information");
1083*0Sstevel@tonic-gate 	}
1084*0Sstevel@tonic-gate }
1085