OpenSolaris_b135/cmd/who/who.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 *	This program analyzes information found in /var/adm/utmpx
 *
 *	Additionally information is gathered from /etc/inittab
 *	if requested.
 *
 *
 *	Syntax:
 *
 *		who am i	Displays info on yourself
 *
 *		who -a		Displays information about All
 *				entries in /var/adm/utmpx
 *
 *		who -b		Displays info on last boot
 *
 *		who -d		Displays info on DEAD PROCESSES
 *
 *		who -H		Displays HEADERS for output
 *
 *		who -l 		Displays info on LOGIN entries
 *
 *		who -m 		Same as who am i
 *
 *		who -p 		Displays info on PROCESSES spawned by init
 *
 *		who -q		Displays short information on
 *				current users who LOGGED ON
 *
 *		who -r		Displays info of current run-level
 *
 *		who -s		Displays requested info in SHORT form
 *
 *		who -t		Displays info on TIME changes
 *
 *		who -T		Displays writeability of each user
 *				(+ writeable, - non-writeable, ? hung)
 *
 *		who -u		Displays LONG info on users
 *				who have LOGGED ON
 */

#define		DATE_FMT	"%b %e %H:%M"

/*
 *  %b	Abbreviated month name
 *  %e	Day of month
 *  %H	hour (24-hour clock)
 *  %M  minute
 */
#include	<errno.h>
#include	<fcntl.h>
#include	<stdio.h>
#include	<string.h>
#include	<sys/types.h>
#include	<unistd.h>
#include	<stdlib.h>
#include	<sys/stat.h>
#include	<time.h>
#include	<utmpx.h>
#include	<locale.h>
#include	<pwd.h>
#include	<limits.h>

static void process(void);
static void ck_file(char *);
static void dump(void);

static struct	utmpx *utmpp;	/* pointer for getutxent()	*/

/*
 * utmpx defines wider fields for user and line.  For compatibility of output,
 * we are limiting these to the old maximums in utmp. Define UTMPX_NAMELEN
 * to use the full lengths.
 */
#ifndef UTMPX_NAMELEN
/* XXX - utmp - fix name length */
#define	NMAX	(_POSIX_LOGIN_NAME_MAX - 1)
#define	LMAX	12
#else /* UTMPX_NAMELEN */
#define	NMAX	(sizeof (utmpp->ut_user))
#define	LMAX	(sizeof (utmpp->ut_line))
#endif

static char	comment[BUFSIZ]; /* holds inittab comment	*/
static char	errmsg[BUFSIZ];	/* used in snprintf for errors	*/
static int	fildes;		/* file descriptor for inittab	*/
static int	Hopt = 0;	/* 1 = who -H			*/
static char	*inittab;	/* ptr to inittab contents	*/
static char	*iinit;		/* index into inittab		*/
static int	justme = 0;	/* 1 = who am i			*/
static struct	tm *lptr;	/* holds user login time	*/
static char	*myname;	/* pointer to invoker's name 	*/
static char	*mytty;		/* holds device user is on	*/
static char	nameval[sizeof (utmpp->ut_user) + 1]; /*  invoker's name */
static int	number = 8;	/* number of users per -q line	*/
static int	optcnt = 0;	/* keeps count of options	*/
static char	outbuf[BUFSIZ];	/* buffer for output		*/
static char	*program;	/* holds name of this program	*/
#ifdef	XPG4
static int	aopt = 0;	/* 1 = who -a			*/
static int	dopt = 0;	/* 1 = who -d			*/
#endif	/* XPG4 */
static int	qopt = 0;	/* 1 = who -q			*/
static int	sopt = 0;	/* 1 = who -s			*/
static struct	stat stbuf;	/* area for stat buffer		*/
static struct	stat *stbufp;	/* ptr to structure		*/
static int	terse = 1;	/* 1 = print terse msgs		*/
static int	Topt = 0;	/* 1 = who -T			*/
static time_t	timnow;		/* holds current time		*/
static int	totlusrs = 0;	/* cntr for users on system	*/
static int	uopt = 0;	/* 1 = who -u			*/
static char	user[sizeof (utmpp->ut_user) + 1]; /* holds user name */
static int	validtype[UTMAXTYPE+1];	/* holds valid types	*/
static int	wrap;		/* flag to indicate wrap	*/
static char	time_buf[128];	/* holds date and time string	*/
static char	*end;		/* used in strtol for end pointer */

int
main(int argc, char **argv)
{
	int	goerr = 0;	/* non-zero indicates cmd error	*/
	int	i;
	int	optsw;		/* switch for while of getopt()	*/

	(void) setlocale(LC_ALL, "");

#if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
#endif
	(void) textdomain(TEXT_DOMAIN);

	validtype[USER_PROCESS] = 1;
	validtype[EMPTY] = 0;
	stbufp = &stbuf;

	/*
	 *	Strip off path name of this command
	 */
	for (i = strlen(argv[0]); i >= 0 && argv[0][i] != '/'; --i);
	if (i >= 0)
		argv[0] += i+1;
	program = argv[0];

	/*
	 *	Buffer stdout for speed
	 */
	setbuf(stdout, outbuf);

	/*
	 *	Retrieve options specified on command line
	 *	XCU4 - add -m option
	 */
	while ((optsw = getopt(argc, argv, "abdHlmn:pqrstTu")) != EOF) {
		optcnt++;
		switch (optsw) {

		case 'a':
			optcnt += 7;
			validtype[BOOT_TIME] = 1;
			validtype[DEAD_PROCESS] = 1;
			validtype[LOGIN_PROCESS] = 1;
			validtype[INIT_PROCESS] = 1;
			validtype[RUN_LVL] = 1;
			validtype[OLD_TIME] = 1;
			validtype[NEW_TIME] = 1;
			validtype[USER_PROCESS] = 1;
#ifdef	XPG4
			aopt = 1;
#endif	/* XPG4 */
			uopt = 1;
			Topt = 1;
			if (!sopt) terse = 0;
			break;

		case 'b':
			validtype[BOOT_TIME] = 1;
			if (!uopt) validtype[USER_PROCESS] = 0;
			break;

		case 'd':
			validtype[DEAD_PROCESS] = 1;
			if (!uopt) validtype[USER_PROCESS] = 0;
#ifdef	XPG4
			dopt = 1;
#endif	/* XPG4 */
			break;

		case 'H':
			optcnt--; /* Don't count Header */
			Hopt = 1;
			break;

		case 'l':
			validtype[LOGIN_PROCESS] = 1;
			if (!uopt) validtype[USER_PROCESS] = 0;
			terse = 0;
			break;
		case 'm':		/* New XCU4 option */
			justme = 1;
			break;

		case 'n':
			errno = 0;
			number = strtol(optarg, &end, 10);
			if (errno != 0 || *end != '\0') {
				(void) fprintf(stderr, gettext(
				    "%s: Invalid numeric argument\n"),
				    program);
				exit(1);
			}
			if (number < 1) {
				(void) fprintf(stderr, gettext(
				    "%s: Number of users per line must "
					"be at least 1\n"), program);
				exit(1);
			}
			break;

		case 'p':
			validtype[INIT_PROCESS] = 1;
			if (!uopt) validtype[USER_PROCESS] = 0;
			break;

		case 'q':
			qopt = 1;
			break;

		case 'r':
			validtype[RUN_LVL] = 1;
			terse = 0;
			if (!uopt) validtype[USER_PROCESS] = 0;
			break;

		case 's':
			sopt = 1;
			terse = 1;
			break;

		case 't':
			validtype[OLD_TIME] = 1;
			validtype[NEW_TIME] = 1;
			if (!uopt) validtype[USER_PROCESS] = 0;
			break;

		case 'T':
			Topt = 1;
#ifdef	XPG4
			terse = 1;	/* XPG4 requires -T */
#else	/* XPG4 */
			terse = 0;
#endif	/* XPG4 */
			break;

		case 'u':
			uopt = 1;
			validtype[USER_PROCESS] = 1;
			if (!sopt) terse = 0;
			break;

		case '?':
			goerr++;
			break;
		default:
			break;
		}
	}
#ifdef	XPG4
	/*
	 * XCU4 changes - check for illegal sopt, Topt & aopt combination
	 */
	if (sopt == 1) {
		terse = 1;
		if (Topt == 1 || aopt == 1)
		goerr++;
	}
#endif	/* XPG4 */

	if (goerr > 0) {
#ifdef	XPG4
		/*
		 * XCU4 - slightly different usage with -s -a & -T
		 */
		(void) fprintf(stderr, gettext("\nUsage:\t%s"), program);
		(void) fprintf(stderr,
		    gettext(" -s [-bdHlmpqrtu] [utmpx_like_file]\n"));

		(void) fprintf(stderr, gettext(
		    "\t%s [-abdHlmpqrtTu] [utmpx_like_file]\n"), program);
#else	/* XPG4 */
		(void) fprintf(stderr, gettext(
		    "\nUsage:\t%s [-abdHlmpqrstTu] [utmpx_like_file]\n"),
		    program);
#endif	/* XPG4 */
		(void) fprintf(stderr,
		    gettext("\t%s -q [-n x] [utmpx_like_file]\n"), program);
		(void) fprintf(stderr, gettext("\t%s [am i]\n"), program);
		/*
		 * XCU4 changes - be explicit with "am i" options
		 */
		(void) fprintf(stderr, gettext("\t%s [am I]\n"), program);
		(void) fprintf(stderr, gettext(
		    "a\tall (bdlprtu options)\n"));
		(void) fprintf(stderr, gettext("b\tboot time\n"));
		(void) fprintf(stderr, gettext("d\tdead processes\n"));
		(void) fprintf(stderr, gettext("H\tprint header\n"));
		(void) fprintf(stderr, gettext("l\tlogin processes\n"));
		(void) fprintf(stderr, gettext(
		    "n #\tspecify number of users per line for -q\n"));
		(void) fprintf(stderr,
		    gettext("p\tprocesses other than getty or users\n"));
		(void) fprintf(stderr, gettext("q\tquick %s\n"), program);
		(void) fprintf(stderr, gettext("r\trun level\n"));
		(void) fprintf(stderr, gettext(
		"s\tshort form of %s (no time since last output or pid)\n"),
		    program);
		(void) fprintf(stderr, gettext("t\ttime changes\n"));
		(void) fprintf(stderr, gettext(
		    "T\tstatus of tty (+ writable, - not writable, "
			"? hung)\n"));
		(void) fprintf(stderr, gettext("u\tuseful information\n"));
		(void) fprintf(stderr,
		    gettext("m\tinformation only about current terminal\n"));
		(void) fprintf(stderr, gettext(
		    "am i\tinformation about current terminal "
			"(same as -m)\n"));
		(void) fprintf(stderr, gettext(
		    "am I\tinformation about current terminal "
			"(same as -m)\n"));
		exit(1);
	}

	/*
	 * XCU4: If -q option ignore all other options
	 */
	if (qopt == 1) {
		Hopt = 0;
		sopt = 0;
		Topt = 0;
		uopt = 0;
		justme = 0;
		validtype[ACCOUNTING] = 0;
		validtype[BOOT_TIME] = 0;
		validtype[DEAD_PROCESS] = 0;
		validtype[LOGIN_PROCESS] = 0;
		validtype[INIT_PROCESS] = 0;
		validtype[RUN_LVL] = 0;
		validtype[OLD_TIME] = 0;
		validtype[NEW_TIME] = 0;
		validtype[USER_PROCESS] = 1;
	}

	if (argc == optind + 1) {
		optcnt++;
		ck_file(argv[optind]);
		(void) utmpxname(argv[optind]);
	}

	/*
	 *	Test for 'who am i' or 'who am I'
	 *	XCU4 - check if justme was already set by -m option
	 */
	if (justme == 1 || (argc == 3 && strcmp(argv[1], "am") == 0 &&
	    ((argv[2][0] == 'i' || argv[2][0] == 'I') &&
		argv[2][1] == '\0'))) {
		justme = 1;
		myname = nameval;
		(void) cuserid(myname);
		if ((mytty = ttyname(fileno(stdin))) == NULL &&
		    (mytty = ttyname(fileno(stdout))) == NULL &&
		    (mytty = ttyname(fileno(stderr))) == NULL) {
			(void) fprintf(stderr, gettext(
			"Must be attached to terminal for 'am I' option\n"));
			(void) fflush(stderr);
			exit(1);
		} else
			mytty += 5; /* bump past "/dev/" */
	}

	if (!terse) {
		if (Hopt)
			(void) printf(gettext(
	"NAME       LINE         TIME          IDLE    PID  COMMENTS\n"));

		timnow = time(0);

		if ((fildes = open("/etc/inittab",
		    O_NONBLOCK|O_RDONLY)) == -1) {
			(void) snprintf(errmsg, sizeof (errmsg),
			    gettext("%s: Cannot open /etc/inittab"), program);
			perror(errmsg);
			exit(errno);
		}

		if (fstat(fildes, stbufp) == -1) {
			(void) snprintf(errmsg, sizeof (errmsg),
			    gettext("%s: Cannot stat /etc/inittab"), program);
			perror(errmsg);
			exit(errno);
		}

		if ((inittab = malloc(stbufp->st_size + 1)) == NULL) {
			(void) snprintf(errmsg, sizeof (errmsg),
			    gettext("%s: Cannot allocate %ld bytes"),
			    program, stbufp->st_size);
			perror(errmsg);
			exit(errno);
		}

		if (read(fildes, inittab, stbufp->st_size)
		    != stbufp->st_size) {
			(void) snprintf(errmsg, sizeof (errmsg),
			    gettext("%s: Error reading /etc/inittab"),
			    program);
			perror(errmsg);
			exit(errno);
		}

		inittab[stbufp->st_size] = '\0';
		iinit = inittab;
	} else {
		if (Hopt) {
#ifdef	XPG4
			if (dopt) {
				(void) printf(gettext(
			"NAME       LINE         TIME		COMMENTS\n"));
			} else {
				(void) printf(
				    gettext("NAME       LINE         TIME\n"));
			}
#else	/* XPG4 */
			(void) printf(
			    gettext("NAME       LINE         TIME\n"));
#endif	/* XPG4 */
		}
	}
	process();

	/*
	 *	'who -q' requires EOL upon exit,
	 *	followed by total line
	 */
	if (qopt)
		(void) printf(gettext("\n# users=%d\n"), totlusrs);
	return (0);
}

static void
dump()
{
	char	device[sizeof (utmpp->ut_line) + 1];
	time_t hr;
	time_t	idle;
	time_t min;
	char	path[sizeof (utmpp->ut_line) + 6];
	int	pexit;
	int	pterm;
	int	rc;
	char	w;	/* writeability indicator */

	/*
	 * Get and check user name
	 */
	if (utmpp->ut_user[0] == '\0')
		(void) strcpy(user, "   .");
	else {
		(void) strncpy(user, utmpp->ut_user, sizeof (user));
		user[sizeof (user) - 1] = '\0';
	}
	totlusrs++;

	/*
	 * Do print in 'who -q' format
	 */
	if (qopt) {
		/*
		 * XCU4 - Use non user macro for correct user count
		 */
		if (((totlusrs - 1) % number) == 0 && totlusrs > 1)
			(void) printf("\n");
		(void) printf("%-*s ", NMAX, user);
		return;
	}


	pexit = (int)' ';
	pterm = (int)' ';

	/*
	 *	Get exit info if applicable
	 */
	if (utmpp->ut_type == RUN_LVL || utmpp->ut_type == DEAD_PROCESS) {
		pterm = utmpp->ut_exit.e_termination;
		pexit = utmpp->ut_exit.e_exit;
	}

	/*
	 *	Massage ut_xtime field
	 */
	lptr = localtime(&utmpp->ut_xtime);
	(void) strftime(time_buf, sizeof (time_buf),
	    dcgettext(NULL, DATE_FMT, LC_TIME), lptr);

	/*
	 *	Get and massage device
	 */
	if (utmpp->ut_line[0] == '\0')
		(void) strcpy(device, "     .");
	else {
		(void) strncpy(device, utmpp->ut_line,
		    sizeof (utmpp->ut_line));
		device[sizeof (utmpp->ut_line)] = '\0';
	}

	/*
	 *	Get writeability if requested
	 *	XCU4 - only print + or - for user processes
	 */
	if (Topt && (utmpp->ut_type == USER_PROCESS)) {
		w = '-';
		(void) strcpy(path, "/dev/");
		(void) strncpy(path + 5, utmpp->ut_line,
		    sizeof (utmpp->ut_line));
		path[5 + sizeof (utmpp->ut_line)] = '\0';

		if ((rc = stat(path, stbufp)) == -1) w = '?';
		else if ((stbufp->st_mode & S_IWOTH) ||
		    (stbufp->st_mode & S_IWGRP))  /* Check group & other */
			w = '+';

	} else
		w = ' ';

	/*
	 *	Print the TERSE portion of the output
	 */
	(void) printf("%-*s %c %-12s %s", NMAX, user, w, device, time_buf);

	if (!terse) {
		/*
		 *	Stat device for idle time
		 *	(Don't complain if you can't)
		 */
		rc = -1;
		if (utmpp->ut_type == USER_PROCESS) {
			(void) strcpy(path, "/dev/");
			(void) strncpy(path + 5, utmpp->ut_line,
			    sizeof (utmpp->ut_line));
			path[5 + sizeof (utmpp->ut_line)] = '\0';
			rc = stat(path, stbufp);
		}
		if (rc != -1) {
			idle = timnow - stbufp->st_mtime;
			hr = idle/3600;
			min = (unsigned)(idle/60)%60;
			if (hr == 0 && min == 0)
				(void) printf(gettext("   .  "));
			else {
				if (hr < 24)
					(void) printf(" %2d:%2.2d", (int)hr,
					    (int)min);
				else
					(void) printf(gettext("  old "));
			}
		}

		/*
		 *	Add PID for verbose output
		 */
		if (utmpp->ut_type != BOOT_TIME &&
		    utmpp->ut_type != RUN_LVL &&
		    utmpp->ut_type != ACCOUNTING)
			(void) printf("  %5ld", utmpp->ut_pid);

		/*
		 *	Handle /etc/inittab comment
		 */
		if (utmpp->ut_type == DEAD_PROCESS) {
			(void) printf(gettext("  id=%4.4s "),
			    utmpp->ut_id);
			(void) printf(gettext("term=%-3d "), pterm);
			(void) printf(gettext("exit=%d  "), pexit);
		} else if (utmpp->ut_type != INIT_PROCESS) {
			/*
			 *	Search for each entry in inittab
			 *	string. Keep our place from
			 *	search to search to try and
			 *	minimize the work. Wrap once if needed
			 *	for each entry.
			 */
			wrap = 0;
			/*
			 *	Look for a line beginning with
			 *	utmpp->ut_id
			 */
			while ((rc = strncmp(utmpp->ut_id, iinit,
			    strcspn(iinit, ":"))) != 0) {
				for (; *iinit != '\n'; iinit++);
				iinit++;

				/*
				 *	Wrap once if necessary to
				 *	find entry in inittab
				 */
				if (*iinit == '\0') {
					if (!wrap) {
						iinit = inittab;
						wrap = 1;
					}
				}
			}

			if (*iinit != '\0') {
				/*
				 *	We found our entry
				 */
				for (iinit++; *iinit != '#' &&
					 *iinit != '\n'; iinit++);
				if (*iinit == '#') {
					for (iinit++; *iinit == ' ' ||
						 *iinit == '\t'; iinit++);
					for (rc = 0; *iinit != '\n'; iinit++)
						comment[rc++] = *iinit;
					comment[rc] = '\0';
				} else
					(void) strcpy(comment, " ");

				(void) printf("  %s", comment);
			} else
				iinit = inittab;	/* Reset pointer */
		}
		if (utmpp->ut_type == INIT_PROCESS)
			(void) printf(gettext("  id=%4.4s"), utmpp->ut_id);
	}
#ifdef	XPG4
	else
		if (dopt && utmpp->ut_type == DEAD_PROCESS) {
			(void) printf(gettext("\tterm=%-3d "), pterm);
			(void) printf(gettext("exit=%d  "), pexit);
		}
#endif	/* XPG4 */


	/*
	 *	Handle RUN_LVL process - If no alt. file - Only one!
	 */
	if (utmpp->ut_type == RUN_LVL) {
		(void) printf("     %c  %5ld  %c", pterm, utmpp->ut_pid,
		    pexit);
		if (optcnt == 1 && !validtype[USER_PROCESS]) {
			(void) printf("\n");
			exit(0);
		}
	}

	/*
	 *	Handle BOOT_TIME process -  If no alt. file - Only one!
	 */
	if (utmpp->ut_type == BOOT_TIME) {
		if (optcnt == 1 && !validtype[USER_PROCESS]) {
			(void) printf("\n");
			exit(0);
		}
	}

	/*
	 *	Get remote host from utmpx structure
	 */
	if (utmpp && utmpp->ut_host[0])
		(void) printf("\t(%.*s)", sizeof (utmpp->ut_host),
		    utmpp->ut_host);

	/*
	 *	Now, put on the trailing EOL
	 */
	(void) printf("\n");
}

static void
process()
{
	struct passwd *pwp;
	int i = 0;
	char *ttname;

	/*
	 *	Loop over each entry in /var/adm/utmpx
	 */

	setutxent();
	while ((utmpp = getutxent()) != NULL) {
#ifdef DEBUG
	(void) printf(
	    "ut_user '%s'\nut_id '%s'\nut_line '%s'\nut_type '%d'\n\n",
	    utmpp->ut_user, utmpp->ut_id, utmpp->ut_line, utmpp->ut_type);
#endif
		if (utmpp->ut_type <= UTMAXTYPE) {
			/*
			 *	Handle "am i"
			 */
			if (justme) {
				if (strncmp(myname, utmpp->ut_user,
				    sizeof (utmpp->ut_user)) == 0 &&
				    strncmp(mytty, utmpp->ut_line,
					sizeof (utmpp->ut_line)) == 0 &&
				    utmpp->ut_type == USER_PROCESS) {
					/*
					 * we have have found ourselves
					 * in the utmp file and the entry
					 * is a user process, this is not
					 * meaningful otherwise
					 *
					 */

					dump();
					exit(0);
				}
				continue;
			}

			/*
			 *	Print the line if we want it
			 */
			if (validtype[utmpp->ut_type]) {
#ifdef	XPG4
				if (utmpp->ut_type == LOGIN_PROCESS) {
					if ((utmpp->ut_line[0] == '\0') ||
					(strcmp(utmpp->ut_user, "LOGIN") != 0))
						continue;
				}
#endif	/* XPG4 */
				dump();
			}
		} else {
			(void) fprintf(stderr,
			    gettext("%s: Error --- entry has ut_type "
				"of %d\n"), program, utmpp->ut_type);
			(void) fprintf(stderr,
			    gettext(" when maximum is %d\n"), UTMAXTYPE);
		}
	}

	/*
	 * If justme is set at this point than the utmp entry
	 * was not found.
	 */
	if (justme) {
		static struct utmpx utmpt;

		pwp = getpwuid(geteuid());
		if (pwp != NULL)
			while (i < (int)sizeof (utmpt.ut_user) &&
			    *pwp->pw_name != 0)
				utmpt.ut_user[i++] = *pwp->pw_name++;

		ttname = ttyname(1);

		i = 0;
		if (ttname != NULL)
			while (i < (int)sizeof (utmpt.ut_line) &&
			    *ttname != 0)
				utmpt.ut_line[i++] = *ttname++;

		utmpt.ut_id[0] = 0;
		utmpt.ut_pid = getpid();
		utmpt.ut_type = USER_PROCESS;
		(void) time(&utmpt.ut_xtime);
		utmpp = &utmpt;
		dump();
		exit(0);
	}
}

/*
 *	This routine checks the following:
 *
 *	1.	File exists
 *
 *	2.	We have read permissions
 *
 *	3.	It is a multiple of utmp entries in size
 *
 *	Failing any of these conditions causes who(1) to
 *	abort processing.
 *
 *	4.	If file is empty we exit right away as there
 *		is no info to report on.
 *
 *	This routine does not check utmpx files.
 */
static void
ck_file(char *name)
{
	struct	stat sbuf;
	int	rc;

	/*
	 *	Does file exist? Do stat to check, and save structure
	 *	so that we can check on the file's size later on.
	 */
	if ((rc = stat(name, &sbuf)) == -1) {
		(void) snprintf(errmsg, sizeof (errmsg),
		    gettext("%s: Cannot stat file '%s'"), program, name);
		perror(errmsg);
		exit(1);
	}

	/*
	 *	The only real way we can be sure we can access the
	 *	file is to try. If we succeed then we close it.
	 */
	if (access(name, R_OK) < 0) {
		(void) snprintf(errmsg, sizeof (errmsg),
		    gettext("%s: Cannot open file '%s'"), program, name);
		perror(errmsg);
		exit(1);
	}

	/*
	 *	If the file is empty, we are all done.
	 */
	if (!sbuf.st_size)
		exit(0);

	/*
	 *	Make sure the file is a utmp file.
	 *	We can only check for size being a multiple of
	 *	utmp structures in length.
	 */
	rc = sbuf.st_size % (int)sizeof (struct utmpx);
	if (rc) {
		(void) fprintf(stderr, gettext("%s: File '%s' is not "
		    "a utmpx file\n"), program, name);
		exit(1);
	}
}