xref: /freebsd/usr.bin/who/who.c (revision 38a999420103f16fc0636a1e1f123e42fb26f1d1)
138a99942STim J. Robbins /*-
238a99942STim J. Robbins  * Copyright (c) 2002 Tim J. Robbins.
338a99942STim J. Robbins  * All rights reserved.
49b50d902SRodney W. Grimes  *
59b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
69b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
79b50d902SRodney W. Grimes  * are met:
89b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
99b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
109b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
119b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
129b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
139b50d902SRodney W. Grimes  *
1438a99942STim J. Robbins  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
159b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
169b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738a99942STim J. Robbins  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
189b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
199b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
209b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
219b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
229b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
239b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
249b50d902SRodney W. Grimes  * SUCH DAMAGE.
259b50d902SRodney W. Grimes  */
269b50d902SRodney W. Grimes 
272c062c85SMark Murray #include <sys/cdefs.h>
282c062c85SMark Murray __FBSDID("$FreeBSD$");
292c062c85SMark Murray 
309b50d902SRodney W. Grimes #include <sys/types.h>
3138a99942STim J. Robbins #include <sys/ioctl.h>
3238a99942STim J. Robbins #include <sys/stat.h>
332c062c85SMark Murray 
340abcff76SPhilippe Charnier #include <err.h>
35e36de81eSAndrey A. Chernov #include <langinfo.h>
36965f310cSAndrey A. Chernov #include <locale.h>
3738a99942STim J. Robbins #include <paths.h>
380abcff76SPhilippe Charnier #include <pwd.h>
390abcff76SPhilippe Charnier #include <stdio.h>
407a19d1bbSDima Dorfman #include <stdlib.h>
410abcff76SPhilippe Charnier #include <string.h>
420abcff76SPhilippe Charnier #include <time.h>
430abcff76SPhilippe Charnier #include <unistd.h>
440abcff76SPhilippe Charnier #include <utmp.h>
459b50d902SRodney W. Grimes 
4638a99942STim J. Robbins static void	heading(void);
4738a99942STim J. Robbins static void	process_utmp(FILE *);
4838a99942STim J. Robbins static void	quick(FILE *);
4938a99942STim J. Robbins static void	row(struct utmp *);
5038a99942STim J. Robbins static int	ttywidth(void);
513f330d7dSWarner Losh static void	usage(void);
5238a99942STim J. Robbins static void	whoami(FILE *);
5338a99942STim J. Robbins 
5438a99942STim J. Robbins static int	Hflag;			/* Write column headings */
5538a99942STim J. Robbins static int	mflag;			/* Show info about current terminal */
5638a99942STim J. Robbins static int	qflag;			/* "Quick" mode */
5738a99942STim J. Robbins static int	sflag;			/* Show name, line, time */
5838a99942STim J. Robbins static int	Tflag;			/* Show terminal state */
5938a99942STim J. Robbins static int	uflag;			/* Show idle time */
600abcff76SPhilippe Charnier 
610abcff76SPhilippe Charnier int
6238a99942STim J. Robbins main(int argc, char *argv[])
639b50d902SRodney W. Grimes {
6438a99942STim J. Robbins 	int ch;
6538a99942STim J. Robbins 	const char *file;
6638a99942STim J. Robbins 	FILE *fp;
679b50d902SRodney W. Grimes 
6838a99942STim J. Robbins 	setlocale(LC_TIME, "");
69965f310cSAndrey A. Chernov 
7038a99942STim J. Robbins 	while ((ch = getopt(argc, argv, "HTabdlmpqrstu")) != -1) {
7138a99942STim J. Robbins 		switch (ch) {
7238a99942STim J. Robbins 		case 'H':		/* Write column headings */
7338a99942STim J. Robbins 			Hflag = 1;
749b50d902SRodney W. Grimes 			break;
7538a99942STim J. Robbins 		case 'T':		/* Show terminal state */
7638a99942STim J. Robbins 			Tflag = 1;
779b50d902SRodney W. Grimes 			break;
7838a99942STim J. Robbins 		case 'm':		/* Show info about current terminal */
7938a99942STim J. Robbins 			mflag = 1;
8038a99942STim J. Robbins 			break;
8138a99942STim J. Robbins 		case 'q':		/* "Quick" mode */
8238a99942STim J. Robbins 			qflag = 1;
8338a99942STim J. Robbins 			break;
8438a99942STim J. Robbins 		case 's':		/* Show name, line, time */
8538a99942STim J. Robbins 			sflag = 1;
8638a99942STim J. Robbins 			break;
8738a99942STim J. Robbins 		case 'u':		/* Show idle time */
8838a99942STim J. Robbins 			uflag = 1;
899b50d902SRodney W. Grimes 			break;
909b50d902SRodney W. Grimes 		default:
910abcff76SPhilippe Charnier 			usage();
9238a99942STim J. Robbins 			/*NOTREACHED*/
939b50d902SRodney W. Grimes 		}
9438a99942STim J. Robbins 	}
9538a99942STim J. Robbins 	argc -= optind;
9638a99942STim J. Robbins 	argv += optind;
9738a99942STim J. Robbins 
9838a99942STim J. Robbins 	if (argc >= 2 && strcmp(argv[0], "am") == 0 &&
9938a99942STim J. Robbins 	    (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) {
10038a99942STim J. Robbins 		/* "who am i" or "who am I", equivalent to -m */
10138a99942STim J. Robbins 		mflag = 1;
10238a99942STim J. Robbins 		argc -= 2;
10338a99942STim J. Robbins 		argv += 2;
10438a99942STim J. Robbins 	}
10538a99942STim J. Robbins 	if (argc > 1)
10638a99942STim J. Robbins 		usage();
10738a99942STim J. Robbins 
10838a99942STim J. Robbins 	if (*argv != NULL)
10938a99942STim J. Robbins 		file = *argv;
11038a99942STim J. Robbins 	else
11138a99942STim J. Robbins 		file = _PATH_UTMP;
11238a99942STim J. Robbins 	if ((fp = fopen(file, "r")) == NULL)
11338a99942STim J. Robbins 		err(1, "%s", file);
11438a99942STim J. Robbins 
11538a99942STim J. Robbins 	if (qflag)
11638a99942STim J. Robbins 		quick(fp);
11738a99942STim J. Robbins 	else {
11838a99942STim J. Robbins 		if (sflag)
11938a99942STim J. Robbins 			Tflag = uflag = 0;
12038a99942STim J. Robbins 		if (Hflag)
12138a99942STim J. Robbins 			heading();
12238a99942STim J. Robbins 		if (mflag)
12338a99942STim J. Robbins 			whoami(fp);
12438a99942STim J. Robbins 		else
12538a99942STim J. Robbins 			process_utmp(fp);
12638a99942STim J. Robbins 	}
12738a99942STim J. Robbins 
12838a99942STim J. Robbins 	fclose(fp);
12938a99942STim J. Robbins 
1309b50d902SRodney W. Grimes 	exit(0);
1319b50d902SRodney W. Grimes }
1329b50d902SRodney W. Grimes 
13338a99942STim J. Robbins void
13438a99942STim J. Robbins usage(void)
1350abcff76SPhilippe Charnier {
13638a99942STim J. Robbins 
13738a99942STim J. Robbins 	fprintf(stderr, "usage: who [-HmqsTu] [am I] [file]\n");
1380abcff76SPhilippe Charnier 	exit(1);
1390abcff76SPhilippe Charnier }
1400abcff76SPhilippe Charnier 
1410abcff76SPhilippe Charnier void
14238a99942STim J. Robbins heading(void)
1439b50d902SRodney W. Grimes {
14438a99942STim J. Robbins 
14538a99942STim J. Robbins 	printf("%-*s ", UT_NAMESIZE, "NAME");
14638a99942STim J. Robbins 	if (Tflag)
14738a99942STim J. Robbins 		printf("S ");
14838a99942STim J. Robbins 	printf("%-*s ", UT_LINESIZE, "LINE");
14938a99942STim J. Robbins 	printf("%-*s ", 12, "TIME");
15038a99942STim J. Robbins 	if (uflag)
15138a99942STim J. Robbins 		printf("IDLE  ");
15238a99942STim J. Robbins 	putchar('\n');
15338a99942STim J. Robbins }
15438a99942STim J. Robbins 
15538a99942STim J. Robbins void
15638a99942STim J. Robbins row(struct utmp *ut)
15738a99942STim J. Robbins {
15838a99942STim J. Robbins 	char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE];
15938a99942STim J. Robbins 	struct stat sb;
16038a99942STim J. Robbins 	time_t idle;
161e36de81eSAndrey A. Chernov 	static int d_first = -1;
16238a99942STim J. Robbins 	struct tm *tm;
16338a99942STim J. Robbins 	char state;
164e36de81eSAndrey A. Chernov 
165e36de81eSAndrey A. Chernov 	if (d_first < 0)
166e36de81eSAndrey A. Chernov 		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
1679b50d902SRodney W. Grimes 
16838a99942STim J. Robbins 	if (Tflag || uflag) {
16938a99942STim J. Robbins 		snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV,
17038a99942STim J. Robbins 			UT_LINESIZE, ut->ut_line);
17138a99942STim J. Robbins 		state = '?';
17238a99942STim J. Robbins 		idle = 0;
17338a99942STim J. Robbins 		if (stat(tty, &sb) == 0) {
17438a99942STim J. Robbins 			state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
17538a99942STim J. Robbins 			    '+' : '-';
17638a99942STim J. Robbins 			idle = time(NULL) - sb.st_mtime;
17738a99942STim J. Robbins 		}
1789b50d902SRodney W. Grimes 	}
1799b50d902SRodney W. Grimes 
18038a99942STim J. Robbins 	printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name);
18138a99942STim J. Robbins 	if (Tflag)
18238a99942STim J. Robbins 		printf("%c ", state);
18338a99942STim J. Robbins 	printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line);
18438a99942STim J. Robbins 	tm = localtime(&ut->ut_time);
18538a99942STim J. Robbins 	strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
18638a99942STim J. Robbins 	printf("%-*s ", 12, buf);
18738a99942STim J. Robbins 	if (uflag) {
18838a99942STim J. Robbins 		if (idle < 60)
18938a99942STim J. Robbins 			printf("  .   ");
19038a99942STim J. Robbins 		else if (idle < 24 * 60 * 60)
19138a99942STim J. Robbins 			printf("%02d:%02d ", (int)(idle / 60 / 60),
19238a99942STim J. Robbins 			    (int)(idle / 60 % 60));
19338a99942STim J. Robbins 		else
19438a99942STim J. Robbins 			printf(" old  ");
19538a99942STim J. Robbins 	}
19638a99942STim J. Robbins 	if (*ut->ut_host != '\0')
19738a99942STim J. Robbins 		printf("(%.*s)", UT_HOSTSIZE, ut->ut_host);
19838a99942STim J. Robbins 	putchar('\n');
19938a99942STim J. Robbins }
2009b50d902SRodney W. Grimes 
20138a99942STim J. Robbins void
20238a99942STim J. Robbins process_utmp(FILE *fp)
20338a99942STim J. Robbins {
20438a99942STim J. Robbins 	struct utmp ut;
20538a99942STim J. Robbins 
20638a99942STim J. Robbins 	while (fread(&ut, sizeof(ut), 1, fp) == 1)
20738a99942STim J. Robbins 		if (*ut.ut_name != '\0')
20838a99942STim J. Robbins 			row(&ut);
20938a99942STim J. Robbins }
21038a99942STim J. Robbins 
21138a99942STim J. Robbins void
21238a99942STim J. Robbins quick(FILE *fp)
21338a99942STim J. Robbins {
21438a99942STim J. Robbins 	struct utmp ut;
21538a99942STim J. Robbins 	int col, ncols, num;
21638a99942STim J. Robbins 
21738a99942STim J. Robbins 	ncols = ttywidth();
21838a99942STim J. Robbins 	col = num = 0;
21938a99942STim J. Robbins 	while (fread(&ut, sizeof(ut), 1, fp) == 1) {
22038a99942STim J. Robbins 		if (*ut.ut_name == '\0')
22138a99942STim J. Robbins 			continue;
22238a99942STim J. Robbins 		printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name);
22338a99942STim J. Robbins 		if (++col < ncols / (UT_NAMESIZE + 1))
22438a99942STim J. Robbins 			putchar(' ');
22538a99942STim J. Robbins 		else {
22638a99942STim J. Robbins 			col = 0;
22738a99942STim J. Robbins 			putchar('\n');
22838a99942STim J. Robbins 		}
22938a99942STim J. Robbins 		num++;
23038a99942STim J. Robbins 	}
23138a99942STim J. Robbins 	if (col != 0)
23238a99942STim J. Robbins 		putchar('\n');
23338a99942STim J. Robbins 
23438a99942STim J. Robbins 	printf("# users = %d\n", num);
23538a99942STim J. Robbins }
23638a99942STim J. Robbins 
23738a99942STim J. Robbins void
23838a99942STim J. Robbins whoami(FILE *fp)
23938a99942STim J. Robbins {
24038a99942STim J. Robbins 	struct utmp ut;
24138a99942STim J. Robbins 	struct passwd *pwd;
24238a99942STim J. Robbins 	const char *name, *p, *tty;
24338a99942STim J. Robbins 
24438a99942STim J. Robbins 	if ((tty = ttyname(STDIN_FILENO)) == NULL)
24538a99942STim J. Robbins 		tty = "tty??";
24638a99942STim J. Robbins 	else if ((p = strrchr(tty, '/')) != NULL)
24738a99942STim J. Robbins 		tty = p + 1;
24838a99942STim J. Robbins 
24938a99942STim J. Robbins 	/* Search utmp for our tty, dump first matching record. */
25038a99942STim J. Robbins 	while (fread(&ut, sizeof(ut), 1, fp) == 1)
25138a99942STim J. Robbins 		if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty,
25238a99942STim J. Robbins 		    UT_LINESIZE) == 0) {
25338a99942STim J. Robbins 			row(&ut);
25438a99942STim J. Robbins 			return;
25538a99942STim J. Robbins 		}
25638a99942STim J. Robbins 
25738a99942STim J. Robbins 	/* Not found; fill the utmp structure with the information we have. */
25838a99942STim J. Robbins 	memset(&ut, 0, sizeof(ut));
25938a99942STim J. Robbins 	if ((pwd = getpwuid(getuid())) != NULL)
26038a99942STim J. Robbins 		name = pwd->pw_name;
26138a99942STim J. Robbins 	else
26238a99942STim J. Robbins 		name = "?";
26338a99942STim J. Robbins 	strncpy(ut.ut_name, name, UT_NAMESIZE);
26438a99942STim J. Robbins 	strncpy(ut.ut_line, tty, UT_LINESIZE);
26538a99942STim J. Robbins 	time(&ut.ut_time);
26638a99942STim J. Robbins 	row(&ut);
26738a99942STim J. Robbins }
26838a99942STim J. Robbins 
26938a99942STim J. Robbins int
27038a99942STim J. Robbins ttywidth(void)
27138a99942STim J. Robbins {
27238a99942STim J. Robbins 	struct winsize ws;
27338a99942STim J. Robbins 	int width;
27438a99942STim J. Robbins 
27538a99942STim J. Robbins 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
27638a99942STim J. Robbins 		width = ws.ws_col;
27738a99942STim J. Robbins 	else
27838a99942STim J. Robbins 		width = 80;
27938a99942STim J. Robbins 
28038a99942STim J. Robbins 	return (width);
2819b50d902SRodney W. Grimes }
282