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