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