1/*	$OpenBSD: who.c,v 1.33 2021/11/11 08:48:48 mestre Exp $	*/
2/*	$NetBSD: who.c,v 1.4 1994/12/07 04:28:49 jtc Exp $	*/
3
4/*
5 * Copyright (c) 1989, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Michael Fischbein.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <paths.h>
39#include <pwd.h>
40#include <utmp.h>
41#include <stdio.h>
42#include <string.h>
43#include <stdlib.h>
44#include <unistd.h>
45#include <time.h>
46#include <err.h>
47
48void  output(struct utmp *);
49void  output_labels(void);
50void  who_am_i(FILE *);
51void  usage(void);
52FILE *file(char *);
53
54int only_current_term;		/* show info about the current terminal only */
55int show_term;			/* show term state */
56int show_idle;			/* show idle time */
57int show_labels;		/* show column labels */
58int show_quick;			/* quick, names only */
59
60#define NAME_WIDTH	8
61#define HOST_WIDTH	45
62
63int hostwidth = HOST_WIDTH;
64char *mytty;
65
66int
67main(int argc, char *argv[])
68{
69	struct utmp usr;
70	FILE *ufp;
71	char *t;
72	int c;
73
74	if (pledge("stdio unveil rpath getpw", NULL) == -1)
75		err(1, "pledge");
76
77	if ((mytty = ttyname(0))) {
78		/* strip any directory component */
79		if ((t = strrchr(mytty, '/')))
80			mytty = t + 1;
81	}
82
83	only_current_term = show_term = show_idle = show_labels = 0;
84	show_quick = 0;
85	while ((c = getopt(argc, argv, "HmqTu")) != -1) {
86		switch (c) {
87		case 'H':
88			show_labels = 1;
89			break;
90		case 'm':
91			only_current_term = 1;
92			break;
93		case 'q':
94			show_quick = 1;
95			break;
96		case 'T':
97			show_term = 1;
98			break;
99		case 'u':
100			show_idle = 1;
101			break;
102		default:
103			usage();
104			/* NOTREACHED */
105		}
106	}
107	argc -= optind;
108	argv += optind;
109
110	if (show_quick) {
111		only_current_term = show_term = show_idle = show_labels = 0;
112	}
113
114	if (show_term)
115		hostwidth -= 2;
116	if (show_idle)
117		hostwidth -= 6;
118
119	if (show_labels)
120		output_labels();
121
122	if (unveil(_PATH_UTMP, "r") == -1)
123		err(1, "unveil %s", _PATH_UTMP);
124	if (show_term || show_idle) {
125		if (unveil(_PATH_DEV, "r") == -1)
126			err(1, "unveil %s", _PATH_DEV);
127	}
128	if (argc == 1) {
129		if (unveil(*argv, "r") == -1)
130			err(1, "unveil %s", *argv);
131	}
132	if (pledge("stdio rpath getpw", NULL) == -1)
133		err(1, "pledge");
134
135	switch (argc) {
136	case 0:					/* who */
137		ufp = file(_PATH_UTMP);
138
139		if (only_current_term) {
140			who_am_i(ufp);
141		} else if (show_quick) {
142			int count = 0;
143
144			while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1) {
145				if (*usr.ut_name && *usr.ut_line) {
146					(void)printf("%-*.*s ", NAME_WIDTH,
147						UT_NAMESIZE, usr.ut_name);
148					if ((++count % 8) == 0)
149						(void) printf("\n");
150				}
151			}
152			if (count % 8)
153				(void) printf("\n");
154			(void) printf ("# users=%d\n", count);
155		} else {
156			/* only entries with both name and line fields */
157			while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
158				if (*usr.ut_name && *usr.ut_line)
159					output(&usr);
160		}
161		break;
162	case 1:					/* who utmp_file */
163		ufp = file(*argv);
164
165		if (only_current_term) {
166			who_am_i(ufp);
167		} else if (show_quick) {
168			int count = 0;
169
170			while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1) {
171				if (*usr.ut_name && *usr.ut_line) {
172					(void)printf("%-*.*s ", NAME_WIDTH,
173						UT_NAMESIZE, usr.ut_name);
174					if ((++count % 8) == 0)
175						(void) printf("\n");
176				}
177			}
178			if (count % 8)
179				(void) printf("\n");
180			(void) printf ("# users=%d\n", count);
181		} else {
182			/* all entries */
183			while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
184				output(&usr);
185		}
186		break;
187	case 2:					/* who am i */
188		ufp = file(_PATH_UTMP);
189		who_am_i(ufp);
190		break;
191	default:
192		usage();
193		/* NOTREACHED */
194	}
195	exit(0);
196}
197
198void
199who_am_i(FILE *ufp)
200{
201	struct utmp usr;
202	struct passwd *pw;
203
204	/* search through the utmp and find an entry for this tty */
205	if (mytty) {
206		while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
207			if (*usr.ut_name && !strcmp(usr.ut_line, mytty)) {
208				output(&usr);
209				return;
210			}
211		/* well, at least we know what the tty is */
212		(void)strncpy(usr.ut_line, mytty, UT_LINESIZE);
213	} else
214		(void)strncpy(usr.ut_line, "tty??", UT_LINESIZE);
215
216	pw = getpwuid(getuid());
217	(void)strncpy(usr.ut_name, pw ? pw->pw_name : "?", UT_NAMESIZE);
218	(void)time(&usr.ut_time);
219	*usr.ut_host = '\0';
220	output(&usr);
221}
222
223void
224output(struct utmp *up)
225{
226	struct stat sb;
227	char line[sizeof(_PATH_DEV) + sizeof (up->ut_line)];
228	char state = '?';
229	static time_t now = 0;
230	time_t idle = 0;
231
232	if (show_term || show_idle) {
233		if (now == 0)
234			time(&now);
235
236		memset(line, 0, sizeof line);
237		strlcpy(line, _PATH_DEV, sizeof line);
238		strlcat(line, up->ut_line, sizeof line);
239
240		if (stat(line, &sb) == 0) {
241			state = (sb.st_mode & 020) ? '+' : '-';
242			idle = now - sb.st_atime;
243		} else {
244			state = '?';
245			idle = 0;
246		}
247	}
248
249	(void)printf("%-*.*s ", NAME_WIDTH, UT_NAMESIZE, up->ut_name);
250
251	if (show_term) {
252		(void)printf("%c ", state);
253	}
254
255	(void)printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, up->ut_line);
256	(void)printf("%.12s ", ctime(&up->ut_time) + 4);
257
258	if (show_idle) {
259		if (idle < 60)
260			(void)printf("  .   ");
261		else if (idle < (24 * 60 * 60))
262			(void)printf("%02d:%02d ",
263				     ((int)idle / (60 * 60)),
264				     ((int)idle % (60 * 60)) / 60);
265		else
266			(void)printf(" old  ");
267	}
268
269	if (*up->ut_host)
270		printf("  (%.*s)", hostwidth, up->ut_host);
271	(void)putchar('\n');
272}
273
274void
275output_labels(void)
276{
277	(void)printf("%-*.*s ", NAME_WIDTH, UT_NAMESIZE, "USER");
278
279	if (show_term)
280		(void)printf("S ");
281
282	(void)printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, "LINE");
283	(void)printf("WHEN         ");
284
285	if (show_idle)
286		(void)printf("IDLE  ");
287
288	(void)printf("  %.*s", hostwidth, "FROM");
289
290	(void)putchar('\n');
291}
292
293FILE *
294file(char *name)
295{
296	FILE *ufp;
297
298	if (!(ufp = fopen(name, "r"))) {
299		err(1, "%s", name);
300		/* NOTREACHED */
301	}
302	if (!show_term && !show_idle) {
303		if (pledge("stdio getpw", NULL) == -1)
304			err(1, "pledge");
305	}
306	return(ufp);
307}
308
309void
310usage(void)
311{
312	(void)fprintf(stderr, "usage: who [-HmqTu] [file]\n       who am i\n");
313	exit(1);
314}
315