xref: /titanic_52/usr/src/cmd/who/who.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /*
34  *	This program analyzes information found in /var/adm/utmpx
35  *
36  *	Additionally information is gathered from /etc/inittab
37  *	if requested.
38  *
39  *
40  *	Syntax:
41  *
42  *		who am i	Displays info on yourself
43  *
44  *		who -a		Displays information about All
45  *				entries in /var/adm/utmpx
46  *
47  *		who -b		Displays info on last boot
48  *
49  *		who -d		Displays info on DEAD PROCESSES
50  *
51  *		who -H		Displays HEADERS for output
52  *
53  *		who -l 		Displays info on LOGIN entries
54  *
55  *		who -m 		Same as who am i
56  *
57  *		who -p 		Displays info on PROCESSES spawned by init
58  *
59  *		who -q		Displays short information on
60  *				current users who LOGGED ON
61  *
62  *		who -r		Displays info of current run-level
63  *
64  *		who -s		Displays requested info in SHORT form
65  *
66  *		who -t		Displays info on TIME changes
67  *
68  *		who -T		Displays writeability of each user
69  *				(+ writeable, - non-writeable, ? hung)
70  *
71  *		who -u		Displays LONG info on users
72  *				who have LOGGED ON
73  */
74 
75 #define		DATE_FMT	"%b %e %H:%M"
76 
77 /*
78  *  %b	Abbreviated month name
79  *  %e	Day of month
80  *  %H	hour (24-hour clock)
81  *  %M  minute
82  */
83 #include	<errno.h>
84 #include	<fcntl.h>
85 #include	<stdio.h>
86 #include	<string.h>
87 #include	<sys/types.h>
88 #include	<unistd.h>
89 #include	<stdlib.h>
90 #include	<sys/stat.h>
91 #include	<time.h>
92 #include	<utmpx.h>
93 #include	<locale.h>
94 #include	<pwd.h>
95 #include	<limits.h>
96 
97 static void process(void);
98 static void ck_file(char *);
99 static void dump(void);
100 
101 extern char *optarg;		/* for getopt()			*/
102 extern int optind;		/* for getopt()			*/
103 extern char *sys_errlist[];	/* error msgs for errno    */
104 static struct	utmpx *utmpp;	/* pointer for getutxent()	*/
105 
106 /*
107  * utmpx defines wider fields for user and line.  For compatibility of output,
108  * we are limiting these to the old maximums in utmp. Define UTMPX_NAMELEN
109  * to use the full lengths.
110  */
111 #ifndef UTMPX_NAMELEN
112 /* XXX - utmp - fix name length */
113 #define	NMAX	(_POSIX_LOGIN_NAME_MAX - 1)
114 #define	LMAX	12
115 #else /* UTMPX_NAMELEN */
116 #define	NMAX	(sizeof (utmpp->ut_user))
117 #define	LMAX	(sizeof (utmpp->ut_line))
118 #endif
119 
120 static char	comment[BUFSIZ]; /* holds inittab comment	*/
121 static char	errmsg[BUFSIZ];	/* used in snprintf for errors	*/
122 static int	fildes;		/* file descriptor for inittab	*/
123 static int	Hopt = 0;	/* 1 = who -H			*/
124 static char	*inittab;	/* ptr to inittab contents	*/
125 static char	*iinit;		/* index into inittab		*/
126 static int	justme = 0;	/* 1 = who am i			*/
127 static struct	tm *lptr;	/* holds user login time	*/
128 static char	*myname;	/* pointer to invoker's name 	*/
129 static char	*mytty;		/* holds device user is on	*/
130 static char	nameval[sizeof (utmpp->ut_user) + 1]; /*  invoker's name */
131 static int	number = 8;	/* number of users per -q line	*/
132 static int	optcnt = 0;	/* keeps count of options	*/
133 static char	outbuf[BUFSIZ];	/* buffer for output		*/
134 static char	*program;	/* holds name of this program	*/
135 #ifdef	XPG4
136 static int	aopt = 0;	/* 1 = who -a			*/
137 static int	dopt = 0;	/* 1 = who -d			*/
138 #endif	/* XPG4 */
139 static int	qopt = 0;	/* 1 = who -q			*/
140 static int	sopt = 0;	/* 1 = who -s			*/
141 static struct	stat stbuf;	/* area for stat buffer		*/
142 static struct	stat *stbufp;	/* ptr to structure		*/
143 static int	terse = 1;	/* 1 = print terse msgs		*/
144 static int	Topt = 0;	/* 1 = who -T			*/
145 static time_t	timnow;		/* holds current time		*/
146 static int	totlusrs = 0;	/* cntr for users on system	*/
147 static int	uopt = 0;	/* 1 = who -u			*/
148 static char	user[sizeof (utmpp->ut_user) + 1]; /* holds user name */
149 static int	validtype[UTMAXTYPE+1];	/* holds valid types	*/
150 static int	wrap;		/* flag to indicate wrap	*/
151 static char	time_buf[128];	/* holds date and time string	*/
152 static char	*end;		/* used in strtol for end pointer */
153 
154 int
155 main(int argc, char **argv)
156 {
157 	int	goerr = 0;	/* non-zero indicates cmd error	*/
158 	int	i;
159 	int	optsw;		/* switch for while of getopt()	*/
160 
161 	(void) setlocale(LC_ALL, "");
162 
163 #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
164 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
165 #endif
166 	(void) textdomain(TEXT_DOMAIN);
167 
168 	validtype[USER_PROCESS] = 1;
169 	validtype[EMPTY] = 0;
170 	stbufp = &stbuf;
171 
172 	/*
173 	 *	Strip off path name of this command
174 	 */
175 	for (i = strlen(argv[0]); i >= 0 && argv[0][i] != '/'; --i);
176 	if (i >= 0)
177 		argv[0] += i+1;
178 	program = argv[0];
179 
180 	/*
181 	 *	Buffer stdout for speed
182 	 */
183 	setbuf(stdout, outbuf);
184 
185 	/*
186 	 *	Retrieve options specified on command line
187 	 *	XCU4 - add -m option
188 	 */
189 	while ((optsw = getopt(argc, argv, "abdHlmn:pqrstTu")) != EOF) {
190 		optcnt++;
191 		switch (optsw) {
192 
193 		case 'a':
194 			optcnt += 7;
195 			validtype[BOOT_TIME] = 1;
196 			validtype[DEAD_PROCESS] = 1;
197 			validtype[LOGIN_PROCESS] = 1;
198 			validtype[INIT_PROCESS] = 1;
199 			validtype[RUN_LVL] = 1;
200 			validtype[OLD_TIME] = 1;
201 			validtype[NEW_TIME] = 1;
202 			validtype[USER_PROCESS] = 1;
203 #ifdef	XPG4
204 			aopt = 1;
205 #endif	/* XPG4 */
206 			uopt = 1;
207 			Topt = 1;
208 			if (!sopt) terse = 0;
209 			break;
210 
211 		case 'b':
212 			validtype[BOOT_TIME] = 1;
213 			if (!uopt) validtype[USER_PROCESS] = 0;
214 			break;
215 
216 		case 'd':
217 			validtype[DEAD_PROCESS] = 1;
218 			if (!uopt) validtype[USER_PROCESS] = 0;
219 #ifdef	XPG4
220 			dopt = 1;
221 #endif	/* XPG4 */
222 			break;
223 
224 		case 'H':
225 			optcnt--; /* Don't count Header */
226 			Hopt = 1;
227 			break;
228 
229 		case 'l':
230 			validtype[LOGIN_PROCESS] = 1;
231 			if (!uopt) validtype[USER_PROCESS] = 0;
232 			terse = 0;
233 			break;
234 		case 'm':		/* New XCU4 option */
235 			justme = 1;
236 			break;
237 
238 		case 'n':
239 			errno = 0;
240 			number = strtol(optarg, &end, 10);
241 			if (errno != 0 || *end != '\0') {
242 				(void) fprintf(stderr, gettext(
243 				    "%s: Invalid numeric argument\n"),
244 				    program);
245 				exit(1);
246 			}
247 			if (number < 1) {
248 				(void) fprintf(stderr, gettext(
249 				    "%s: Number of users per line must "
250 					"be at least 1\n"), program);
251 				exit(1);
252 			}
253 			break;
254 
255 		case 'p':
256 			validtype[INIT_PROCESS] = 1;
257 			if (!uopt) validtype[USER_PROCESS] = 0;
258 			break;
259 
260 		case 'q':
261 			qopt = 1;
262 			break;
263 
264 		case 'r':
265 			validtype[RUN_LVL] = 1;
266 			terse = 0;
267 			if (!uopt) validtype[USER_PROCESS] = 0;
268 			break;
269 
270 		case 's':
271 			sopt = 1;
272 			terse = 1;
273 			break;
274 
275 		case 't':
276 			validtype[OLD_TIME] = 1;
277 			validtype[NEW_TIME] = 1;
278 			if (!uopt) validtype[USER_PROCESS] = 0;
279 			break;
280 
281 		case 'T':
282 			Topt = 1;
283 #ifdef	XPG4
284 			terse = 1;	/* XPG4 requires -T */
285 #else	/* XPG4 */
286 			terse = 0;
287 #endif	/* XPG4 */
288 			break;
289 
290 		case 'u':
291 			uopt = 1;
292 			validtype[USER_PROCESS] = 1;
293 			if (!sopt) terse = 0;
294 			break;
295 
296 		case '?':
297 			goerr++;
298 			break;
299 		default:
300 			break;
301 		}
302 	}
303 #ifdef	XPG4
304 	/*
305 	 * XCU4 changes - check for illegal sopt, Topt & aopt combination
306 	 */
307 	if (sopt == 1) {
308 		terse = 1;
309 		if (Topt == 1 || aopt == 1)
310 		goerr++;
311 	}
312 #endif	/* XPG4 */
313 
314 	if (goerr > 0) {
315 #ifdef	XPG4
316 		/*
317 		 * XCU4 - slightly different usage with -s -a & -T
318 		 */
319 		(void) fprintf(stderr, gettext("\nUsage:\t%s"), program);
320 		(void) fprintf(stderr,
321 		    gettext(" -s [-bdHlmpqrtu] [utmpx_like_file]\n"));
322 
323 		(void) fprintf(stderr, gettext(
324 		    "\t%s [-abdHlmpqrtTu] [utmpx_like_file]\n"), program);
325 #else	/* XPG4 */
326 		(void) fprintf(stderr, gettext(
327 		    "\nUsage:\t%s [-abdHlmpqrstTu] [utmpx_like_file]\n"),
328 		    program);
329 #endif	/* XPG4 */
330 		(void) fprintf(stderr,
331 		    gettext("\t%s -q [-n x] [utmpx_like_file]\n"), program);
332 		(void) fprintf(stderr, gettext("\t%s [am i]\n"), program);
333 		/*
334 		 * XCU4 changes - be explicit with "am i" options
335 		 */
336 		(void) fprintf(stderr, gettext("\t%s [am I]\n"), program);
337 		(void) fprintf(stderr, gettext(
338 		    "a\tall (bdlprtu options)\n"));
339 		(void) fprintf(stderr, gettext("b\tboot time\n"));
340 		(void) fprintf(stderr, gettext("d\tdead processes\n"));
341 		(void) fprintf(stderr, gettext("H\tprint header\n"));
342 		(void) fprintf(stderr, gettext("l\tlogin processes\n"));
343 		(void) fprintf(stderr, gettext(
344 		    "n #\tspecify number of users per line for -q\n"));
345 		(void) fprintf(stderr,
346 		    gettext("p\tprocesses other than getty or users\n"));
347 		(void) fprintf(stderr, gettext("q\tquick %s\n"), program);
348 		(void) fprintf(stderr, gettext("r\trun level\n"));
349 		(void) fprintf(stderr, gettext(
350 		"s\tshort form of %s (no time since last output or pid)\n"),
351 		    program);
352 		(void) fprintf(stderr, gettext("t\ttime changes\n"));
353 		(void) fprintf(stderr, gettext(
354 		    "T\tstatus of tty (+ writable, - not writable, "
355 			"? hung)\n"));
356 		(void) fprintf(stderr, gettext("u\tuseful information\n"));
357 		(void) fprintf(stderr,
358 		    gettext("m\tinformation only about current terminal\n"));
359 		(void) fprintf(stderr, gettext(
360 		    "am i\tinformation about current terminal "
361 			"(same as -m)\n"));
362 		(void) fprintf(stderr, gettext(
363 		    "am I\tinformation about current terminal "
364 			"(same as -m)\n"));
365 		exit(1);
366 	}
367 
368 	/*
369 	 * XCU4: If -q option ignore all other options
370 	 */
371 	if (qopt == 1) {
372 		Hopt = 0;
373 		sopt = 0;
374 		Topt = 0;
375 		uopt = 0;
376 		justme = 0;
377 		validtype[ACCOUNTING] = 0;
378 		validtype[BOOT_TIME] = 0;
379 		validtype[DEAD_PROCESS] = 0;
380 		validtype[LOGIN_PROCESS] = 0;
381 		validtype[INIT_PROCESS] = 0;
382 		validtype[RUN_LVL] = 0;
383 		validtype[OLD_TIME] = 0;
384 		validtype[NEW_TIME] = 0;
385 		validtype[USER_PROCESS] = 1;
386 	}
387 
388 	if (argc == optind + 1) {
389 		optcnt++;
390 		ck_file(argv[optind]);
391 		(void) utmpxname(argv[optind]);
392 	}
393 
394 	/*
395 	 *	Test for 'who am i' or 'who am I'
396 	 *	XCU4 - check if justme was already set by -m option
397 	 */
398 	if (justme == 1 || (argc == 3 && strcmp(argv[1], "am") == 0 &&
399 	    ((argv[2][0] == 'i' || argv[2][0] == 'I') &&
400 		argv[2][1] == '\0'))) {
401 		justme = 1;
402 		myname = nameval;
403 		(void) cuserid(myname);
404 		if ((mytty = ttyname(fileno(stdin))) == NULL &&
405 		    (mytty = ttyname(fileno(stdout))) == NULL &&
406 		    (mytty = ttyname(fileno(stderr))) == NULL) {
407 			(void) fprintf(stderr, gettext(
408 			"Must be attached to terminal for 'am I' option\n"));
409 			(void) fflush(stderr);
410 			exit(1);
411 		} else
412 			mytty += 5; /* bump past "/dev/" */
413 	}
414 
415 	if (!terse) {
416 		if (Hopt)
417 			(void) printf(gettext(
418 	"NAME       LINE         TIME          IDLE    PID  COMMENTS\n"));
419 
420 		timnow = time(0);
421 
422 		if ((fildes = open("/etc/inittab",
423 		    O_NONBLOCK|O_RDONLY)) == -1) {
424 			(void) snprintf(errmsg, sizeof (errmsg),
425 			    gettext("%s: Cannot open /etc/inittab"), program);
426 			perror(errmsg);
427 			exit(errno);
428 		}
429 
430 		if (fstat(fildes, stbufp) == -1) {
431 			(void) snprintf(errmsg, sizeof (errmsg),
432 			    gettext("%s: Cannot stat /etc/inittab"), program);
433 			perror(errmsg);
434 			exit(errno);
435 		}
436 
437 		if ((inittab = malloc(stbufp->st_size + 1)) == NULL) {
438 			(void) snprintf(errmsg, sizeof (errmsg),
439 			    gettext("%s: Cannot allocate %ld bytes"),
440 			    program, stbufp->st_size);
441 			perror(errmsg);
442 			exit(errno);
443 		}
444 
445 		if (read(fildes, inittab, stbufp->st_size)
446 		    != stbufp->st_size) {
447 			(void) snprintf(errmsg, sizeof (errmsg),
448 			    gettext("%s: Error reading /etc/inittab"),
449 			    program);
450 			perror(errmsg);
451 			exit(errno);
452 		}
453 
454 		inittab[stbufp->st_size] = '\0';
455 		iinit = inittab;
456 	} else {
457 		if (Hopt) {
458 #ifdef	XPG4
459 			if (dopt) {
460 				(void) printf(gettext(
461 			"NAME       LINE         TIME		COMMENTS\n"));
462 			} else {
463 				(void) printf(
464 				    gettext("NAME       LINE         TIME\n"));
465 			}
466 #else	/* XPG4 */
467 			(void) printf(
468 			    gettext("NAME       LINE         TIME\n"));
469 #endif	/* XPG4 */
470 		}
471 	}
472 	process();
473 
474 	/*
475 	 *	'who -q' requires EOL upon exit,
476 	 *	followed by total line
477 	 */
478 	if (qopt)
479 		(void) printf(gettext("\n# users=%d\n"), totlusrs);
480 	return (0);
481 }
482 
483 static void
484 dump()
485 {
486 	char	device[sizeof (utmpp->ut_line) + 1];
487 	time_t hr;
488 	time_t	idle;
489 	time_t min;
490 	char	path[sizeof (utmpp->ut_line) + 6];
491 	int	pexit;
492 	int	pterm;
493 	int	rc;
494 	char	w;	/* writeability indicator */
495 
496 	/*
497 	 * Get and check user name
498 	 */
499 	if (utmpp->ut_user[0] == '\0')
500 		(void) strcpy(user, "   .");
501 	else {
502 		(void) strncpy(user, utmpp->ut_user, sizeof (user));
503 		user[sizeof (user) - 1] = '\0';
504 	}
505 	totlusrs++;
506 
507 	/*
508 	 * Do print in 'who -q' format
509 	 */
510 	if (qopt) {
511 		/*
512 		 * XCU4 - Use non user macro for correct user count
513 		 */
514 		if (((totlusrs - 1) % number) == 0 && totlusrs > 1)
515 			(void) printf("\n");
516 		(void) printf("%-*s ", NMAX, user);
517 		return;
518 	}
519 
520 
521 	pexit = (int)' ';
522 	pterm = (int)' ';
523 
524 	/*
525 	 *	Get exit info if applicable
526 	 */
527 	if (utmpp->ut_type == RUN_LVL || utmpp->ut_type == DEAD_PROCESS) {
528 		pterm = utmpp->ut_exit.e_termination;
529 		pexit = utmpp->ut_exit.e_exit;
530 	}
531 
532 	/*
533 	 *	Massage ut_xtime field
534 	 */
535 	lptr = localtime(&utmpp->ut_xtime);
536 	(void) strftime(time_buf, sizeof (time_buf),
537 	    dcgettext(NULL, DATE_FMT, LC_TIME), lptr);
538 
539 	/*
540 	 *	Get and massage device
541 	 */
542 	if (utmpp->ut_line[0] == '\0')
543 		(void) strcpy(device, "     .");
544 	else {
545 		(void) strncpy(device, utmpp->ut_line,
546 		    sizeof (utmpp->ut_line));
547 		device[sizeof (utmpp->ut_line)] = '\0';
548 	}
549 
550 	/*
551 	 *	Get writeability if requested
552 	 *	XCU4 - only print + or - for user processes
553 	 */
554 	if (Topt && (utmpp->ut_type == USER_PROCESS)) {
555 		w = '-';
556 		(void) strcpy(path, "/dev/");
557 		(void) strncpy(path + 5, utmpp->ut_line,
558 		    sizeof (utmpp->ut_line));
559 		path[5 + sizeof (utmpp->ut_line)] = '\0';
560 
561 		if ((rc = stat(path, stbufp)) == -1) w = '?';
562 		else if ((stbufp->st_mode & S_IWOTH) ||
563 		    (stbufp->st_mode & S_IWGRP))  /* Check group & other */
564 			w = '+';
565 
566 	} else
567 		w = ' ';
568 
569 	/*
570 	 *	Print the TERSE portion of the output
571 	 */
572 	(void) printf("%-*s %c %-12s %s", NMAX, user, w, device, time_buf);
573 
574 	if (!terse) {
575 		(void) strcpy(path, "/dev/");
576 		(void) strncpy(path + 5, utmpp->ut_line,
577 		    sizeof (utmpp->ut_line));
578 		path[5 + sizeof (utmpp->ut_line)] = '\0';
579 
580 		/*
581 		 *	Stat device for idle time
582 		 *	(Don't complain if you can't)
583 		 */
584 		if ((rc = stat(path, stbufp)) != -1) {
585 			idle = timnow - stbufp->st_mtime;
586 			hr = idle/3600;
587 			min = (unsigned)(idle/60)%60;
588 			if (hr == 0 && min == 0)
589 				(void) printf(gettext("   .  "));
590 			else {
591 				if (hr < 24)
592 					(void) printf(" %2d:%2.2d", (int)hr,
593 					    (int)min);
594 				else
595 					(void) printf(gettext("  old "));
596 			}
597 		}
598 
599 		/*
600 		 *	Add PID for verbose output
601 		 */
602 		if (utmpp->ut_type != BOOT_TIME &&
603 		    utmpp->ut_type != RUN_LVL &&
604 		    utmpp->ut_type != ACCOUNTING)
605 			(void) printf("  %5ld", utmpp->ut_pid);
606 
607 		/*
608 		 *	Handle /etc/inittab comment
609 		 */
610 		if (utmpp->ut_type == DEAD_PROCESS) {
611 			(void) printf(gettext("  id=%4.4s "),
612 			    utmpp->ut_id);
613 			(void) printf(gettext("term=%-3d "), pterm);
614 			(void) printf(gettext("exit=%d  "), pexit);
615 		} else if (utmpp->ut_type != INIT_PROCESS) {
616 			/*
617 			 *	Search for each entry in inittab
618 			 *	string. Keep our place from
619 			 *	search to search to try and
620 			 *	minimize the work. Wrap once if needed
621 			 *	for each entry.
622 			 */
623 			wrap = 0;
624 			/*
625 			 *	Look for a line beginning with
626 			 *	utmpp->ut_id
627 			 */
628 			while ((rc = strncmp(utmpp->ut_id, iinit,
629 			    strcspn(iinit, ":"))) != 0) {
630 				for (; *iinit != '\n'; iinit++);
631 				iinit++;
632 
633 				/*
634 				 *	Wrap once if necessary to
635 				 *	find entry in inittab
636 				 */
637 				if (*iinit == '\0') {
638 					if (!wrap) {
639 						iinit = inittab;
640 						wrap = 1;
641 					}
642 				}
643 			}
644 
645 			if (*iinit != '\0') {
646 				/*
647 				 *	We found our entry
648 				 */
649 				for (iinit++; *iinit != '#' &&
650 					 *iinit != '\n'; iinit++);
651 				if (*iinit == '#') {
652 					for (iinit++; *iinit == ' ' ||
653 						 *iinit == '\t'; iinit++);
654 					for (rc = 0; *iinit != '\n'; iinit++)
655 						comment[rc++] = *iinit;
656 					comment[rc] = '\0';
657 				} else
658 					(void) strcpy(comment, " ");
659 
660 				(void) printf("  %s", comment);
661 			} else
662 				iinit = inittab;	/* Reset pointer */
663 		}
664 		if (utmpp->ut_type == INIT_PROCESS)
665 			(void) printf(gettext("  id=%4.4s"), utmpp->ut_id);
666 	}
667 #ifdef	XPG4
668 	else
669 		if (dopt && utmpp->ut_type == DEAD_PROCESS) {
670 			(void) printf(gettext("\tterm=%-3d "), pterm);
671 			(void) printf(gettext("exit=%d  "), pexit);
672 		}
673 #endif	/* XPG4 */
674 
675 
676 	/*
677 	 *	Handle RUN_LVL process - If no alt. file - Only one!
678 	 */
679 	if (utmpp->ut_type == RUN_LVL) {
680 		(void) printf("     %c  %5ld  %c", pterm, utmpp->ut_pid,
681 		    pexit);
682 		if (optcnt == 1 && !validtype[USER_PROCESS]) {
683 			(void) printf("\n");
684 			exit(0);
685 		}
686 	}
687 
688 	/*
689 	 *	Handle BOOT_TIME process -  If no alt. file - Only one!
690 	 */
691 	if (utmpp->ut_type == BOOT_TIME) {
692 		if (optcnt == 1 && !validtype[USER_PROCESS]) {
693 			(void) printf("\n");
694 			exit(0);
695 		}
696 	}
697 
698 	/*
699 	 *	Get remote host from utmpx structure
700 	 */
701 	if (utmpp && utmpp->ut_host[0])
702 		(void) printf("\t(%.*s)", sizeof (utmpp->ut_host),
703 		    utmpp->ut_host);
704 
705 	/*
706 	 *	Now, put on the trailing EOL
707 	 */
708 	(void) printf("\n");
709 }
710 
711 static void
712 process()
713 {
714 	struct passwd *pwp;
715 	int i = 0;
716 	char *ttname;
717 
718 	/*
719 	 *	Loop over each entry in /var/adm/utmpx
720 	 */
721 
722 	setutxent();
723 	while ((utmpp = getutxent()) != NULL) {
724 #ifdef DEBUG
725 	(void) printf(
726 	    "ut_user '%s'\nut_id '%s'\nut_line '%s'\nut_type '%d'\n\n",
727 	    utmpp->ut_user, utmpp->ut_id, utmpp->ut_line, utmpp->ut_type);
728 #endif
729 		if (utmpp->ut_type <= UTMAXTYPE) {
730 			/*
731 			 *	Handle "am i"
732 			 */
733 			if (justme) {
734 				if (strncmp(myname, utmpp->ut_user,
735 				    sizeof (utmpp->ut_user)) == 0 &&
736 				    strncmp(mytty, utmpp->ut_line,
737 					sizeof (utmpp->ut_line)) == 0 &&
738 				    utmpp->ut_type == USER_PROCESS) {
739 					/*
740 					 * we have have found ourselves
741 					 * in the utmp file and the entry
742 					 * is a user process, this is not
743 					 * meaningful otherwise
744 					 *
745 					 */
746 
747 					dump();
748 					exit(0);
749 				}
750 				continue;
751 			}
752 
753 			/*
754 			 *	Print the line if we want it
755 			 */
756 			if (validtype[utmpp->ut_type]) {
757 #ifdef	XPG4
758 				if (utmpp->ut_type == LOGIN_PROCESS) {
759 					if ((utmpp->ut_line[0] == '\0') ||
760 					(strcmp(utmpp->ut_user, "LOGIN") != 0))
761 						continue;
762 				}
763 #endif	/* XPG4 */
764 				dump();
765 			}
766 		} else {
767 			(void) fprintf(stderr,
768 			    gettext("%s: Error --- entry has ut_type "
769 				"of %d\n"), program, utmpp->ut_type);
770 			(void) fprintf(stderr,
771 			    gettext(" when maximum is %d\n"), UTMAXTYPE);
772 		}
773 	}
774 
775 	/*
776 	 * If justme is set at this point than the utmp entry
777 	 * was not found.
778 	 */
779 	if (justme) {
780 		static struct utmpx utmpt;
781 
782 		pwp = getpwuid(geteuid());
783 		if (pwp != NULL)
784 			while (i < (int)sizeof (utmpt.ut_user) &&
785 			    *pwp->pw_name != 0)
786 				utmpt.ut_user[i++] = *pwp->pw_name++;
787 
788 		ttname = ttyname(1);
789 
790 		i = 0;
791 		if (ttname != NULL)
792 			while (i < (int)sizeof (utmpt.ut_line) &&
793 			    *ttname != 0)
794 				utmpt.ut_line[i++] = *ttname++;
795 
796 		utmpt.ut_id[0] = 0;
797 		utmpt.ut_pid = getpid();
798 		utmpt.ut_type = USER_PROCESS;
799 		(void) time(&utmpt.ut_xtime);
800 		utmpp = &utmpt;
801 		dump();
802 		exit(0);
803 	}
804 }
805 
806 /*
807  *	This routine checks the following:
808  *
809  *	1.	File exists
810  *
811  *	2.	We have read permissions
812  *
813  *	3.	It is a multiple of utmp entries in size
814  *
815  *	Failing any of these conditions causes who(1) to
816  *	abort processing.
817  *
818  *	4.	If file is empty we exit right away as there
819  *		is no info to report on.
820  *
821  *	This routine does not check utmpx files.
822  */
823 static void
824 ck_file(char *name)
825 {
826 	struct	stat sbuf;
827 	int	rc;
828 
829 	/*
830 	 *	Does file exist? Do stat to check, and save structure
831 	 *	so that we can check on the file's size later on.
832 	 */
833 	if ((rc = stat(name, &sbuf)) == -1) {
834 		(void) snprintf(errmsg, sizeof (errmsg),
835 		    gettext("%s: Cannot stat file '%s'"), program, name);
836 		perror(errmsg);
837 		exit(1);
838 	}
839 
840 	/*
841 	 *	The only real way we can be sure we can access the
842 	 *	file is to try. If we succeed then we close it.
843 	 */
844 	if (access(name, R_OK) < 0) {
845 		(void) snprintf(errmsg, sizeof (errmsg),
846 		    gettext("%s: Cannot open file '%s'"), program, name);
847 		perror(errmsg);
848 		exit(1);
849 	}
850 
851 	/*
852 	 *	If the file is empty, we are all done.
853 	 */
854 	if (!sbuf.st_size)
855 		exit(0);
856 
857 	/*
858 	 *	Make sure the file is a utmp file.
859 	 *	We can only check for size being a multiple of
860 	 *	utmp structures in length.
861 	 */
862 	rc = sbuf.st_size % (int)sizeof (struct utmpx);
863 	if (rc) {
864 		(void) fprintf(stderr, gettext("%s: File '%s' is not "
865 		    "a utmpx file\n"), program, name);
866 		exit(1);
867 	}
868 }
869