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