xref: /openbsd-src/usr.bin/who/who.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: who.c,v 1.27 2015/10/21 16:06:57 millert 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 #include <locale.h>
48 
49 void  output(struct utmp *);
50 void  output_labels(void);
51 void  who_am_i(FILE *);
52 void  usage(void);
53 FILE *file(char *);
54 
55 int only_current_term;		/* show info about the current terminal only */
56 int show_term;			/* show term state */
57 int show_idle;			/* show idle time */
58 int show_labels;		/* show column labels */
59 int show_quick;			/* quick, names only */
60 
61 #define NAME_WIDTH	8
62 #define HOST_WIDTH	45
63 
64 int hostwidth = HOST_WIDTH;
65 char *mytty;
66 
67 int
68 main(int argc, char *argv[])
69 {
70 	struct utmp usr;
71 	FILE *ufp;
72 	char *t;
73 	int c;
74 
75 	setlocale(LC_ALL, "");
76 
77 	if (pledge("stdio rpath getpw", NULL) == -1)
78 		err(1, "pledge");
79 
80 	if ((mytty = ttyname(0))) {
81 		/* strip any directory component */
82 		if ((t = strrchr(mytty, '/')))
83 			mytty = t + 1;
84 	}
85 
86 	only_current_term = show_term = show_idle = show_labels = 0;
87 	show_quick = 0;
88 	while ((c = getopt(argc, argv, "HmqTu")) != -1) {
89 		switch (c) {
90 		case 'H':
91 			show_labels = 1;
92 			break;
93 		case 'm':
94 			only_current_term = 1;
95 			break;
96 		case 'q':
97 			show_quick = 1;
98 			break;
99 		case 'T':
100 			show_term = 1;
101 			break;
102 		case 'u':
103 			show_idle = 1;
104 			break;
105 		default:
106 			usage();
107 			/* NOTREACHED */
108 		}
109 	}
110 	argc -= optind;
111 	argv += optind;
112 
113 	if (show_quick) {
114 		only_current_term = show_term = show_idle = show_labels = 0;
115 	}
116 
117 	if (show_term)
118 		hostwidth -= 2;
119 	if (show_idle)
120 		hostwidth -= 6;
121 
122 	if (show_labels)
123 		output_labels();
124 
125 	switch (argc) {
126 	case 0:					/* who */
127 		ufp = file(_PATH_UTMP);
128 
129 		if (only_current_term) {
130 			who_am_i(ufp);
131 		} else if (show_quick) {
132 			int count = 0;
133 
134 			while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1) {
135 				if (*usr.ut_name && *usr.ut_line) {
136 					(void)printf("%-*.*s ", NAME_WIDTH,
137 						UT_NAMESIZE, usr.ut_name);
138 					if ((++count % 8) == 0)
139 						(void) printf("\n");
140 				}
141 			}
142 			if (count % 8)
143 				(void) printf("\n");
144 			(void) printf ("# users=%d\n", count);
145 		} else {
146 			/* only entries with both name and line fields */
147 			while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
148 				if (*usr.ut_name && *usr.ut_line)
149 					output(&usr);
150 		}
151 		break;
152 	case 1:					/* who utmp_file */
153 		ufp = file(*argv);
154 
155 		if (only_current_term) {
156 			who_am_i(ufp);
157 		} else if (show_quick) {
158 			int count = 0;
159 
160 			while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1) {
161 				if (*usr.ut_name && *usr.ut_line) {
162 					(void)printf("%-*.*s ", NAME_WIDTH,
163 						UT_NAMESIZE, usr.ut_name);
164 					if ((++count % 8) == 0)
165 						(void) printf("\n");
166 				}
167 			}
168 			if (count % 8)
169 				(void) printf("\n");
170 			(void) printf ("# users=%d\n", count);
171 		} else {
172 			/* all entries */
173 			while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
174 				output(&usr);
175 		}
176 		break;
177 	case 2:					/* who am i */
178 		ufp = file(_PATH_UTMP);
179 		who_am_i(ufp);
180 		break;
181 	default:
182 		usage();
183 		/* NOTREACHED */
184 	}
185 	exit(0);
186 }
187 
188 void
189 who_am_i(FILE *ufp)
190 {
191 	struct utmp usr;
192 	struct passwd *pw;
193 
194 	/* search through the utmp and find an entry for this tty */
195 	if (mytty) {
196 		while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
197 			if (*usr.ut_name && !strcmp(usr.ut_line, mytty)) {
198 				output(&usr);
199 				return;
200 			}
201 		/* well, at least we know what the tty is */
202 		(void)strncpy(usr.ut_line, mytty, UT_LINESIZE);
203 	} else
204 		(void)strncpy(usr.ut_line, "tty??", UT_LINESIZE);
205 
206 	pw = getpwuid(getuid());
207 	(void)strncpy(usr.ut_name, pw ? pw->pw_name : "?", UT_NAMESIZE);
208 	(void)time(&usr.ut_time);
209 	*usr.ut_host = '\0';
210 	output(&usr);
211 }
212 
213 void
214 output(struct utmp *up)
215 {
216 	struct stat sb;
217 	char line[sizeof(_PATH_DEV) + sizeof (up->ut_line)];
218 	char state = '?';
219 	static time_t now = 0;
220 	time_t idle = 0;
221 
222 	if (show_term || show_idle) {
223 		if (now == 0)
224 			time(&now);
225 
226 		memset(line, 0, sizeof line);
227 		strlcpy(line, _PATH_DEV, sizeof line);
228 		strlcat(line, up->ut_line, sizeof line);
229 
230 		if (stat(line, &sb) == 0) {
231 			state = (sb.st_mode & 020) ? '+' : '-';
232 			idle = now - sb.st_atime;
233 		} else {
234 			state = '?';
235 			idle = 0;
236 		}
237 
238 	}
239 
240 	(void)printf("%-*.*s ", NAME_WIDTH, UT_NAMESIZE, up->ut_name);
241 
242 	if (show_term) {
243 		(void)printf("%c ", state);
244 	}
245 
246 	(void)printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, up->ut_line);
247 	(void)printf("%.12s ", ctime(&up->ut_time) + 4);
248 
249 	if (show_idle) {
250 		if (idle < 60)
251 			(void)printf("  .   ");
252 		else if (idle < (24 * 60 * 60))
253 			(void)printf("%02d:%02d ",
254 				     ((int)idle / (60 * 60)),
255 				     ((int)idle % (60 * 60)) / 60);
256 		else
257 			(void)printf(" old  ");
258 	}
259 
260 	if (*up->ut_host)
261 		printf("  (%.*s)", hostwidth, up->ut_host);
262 	(void)putchar('\n');
263 }
264 
265 void
266 output_labels(void)
267 {
268 	(void)printf("%-*.*s ", NAME_WIDTH, UT_NAMESIZE, "USER");
269 
270 	if (show_term)
271 		(void)printf("S ");
272 
273 	(void)printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, "LINE");
274 	(void)printf("WHEN         ");
275 
276 	if (show_idle)
277 		(void)printf("IDLE  ");
278 
279 	(void)printf("  %.*s", hostwidth, "FROM");
280 
281 	(void)putchar('\n');
282 }
283 
284 FILE *
285 file(char *name)
286 {
287 	FILE *ufp;
288 
289 	if (!(ufp = fopen(name, "r"))) {
290 		err(1, "%s", name);
291 		/* NOTREACHED */
292 	}
293 	if (show_term || show_idle) {
294 		if (pledge("stdio rpath getpw", NULL) == -1)
295 			err(1, "pledge");
296 	} else {
297 		if (pledge("stdio getpw", NULL) == -1)
298 			err(1, "pledge");
299 	}
300 	return(ufp);
301 }
302 
303 void
304 usage(void)
305 {
306 	(void)fprintf(stderr, "usage: who [-HmqTu] [file]\n       who am i\n");
307 	exit(1);
308 }
309