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