1 /*
2  * who - who is on the system
3  *
4  * Gunnar Ritter, Freiburg i. Br., Germany, August 2002.
5  */
6 /*
7  * Copyright (c) 2003 Gunnar Ritter
8  *
9  * This software is provided 'as-is', without any express or implied
10  * warranty. In no event will the authors be held liable for any damages
11  * arising from the use of this software.
12  *
13  * Permission is granted to anyone to use this software for any purpose,
14  * including commercial applications, and to alter it and redistribute
15  * it freely, subject to the following restrictions:
16  *
17  * 1. The origin of this software must not be misrepresented; you must not
18  *    claim that you wrote the original software. If you use this software
19  *    in a product, an acknowledgment in the product documentation would be
20  *    appreciated but is not required.
21  *
22  * 2. Altered source versions must be plainly marked as such, and must not be
23  *    misrepresented as being the original software.
24  *
25  * 3. This notice may not be removed or altered from any source distribution.
26  */
27 
28 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
29 #define	USED	__attribute__ ((used))
30 #elif defined __GNUC__
31 #define	USED	__attribute__ ((unused))
32 #else
33 #define	USED
34 #endif
35 #ifdef	SUS
36 static const char sccsid[] USED = "@(#)who_sus.sl	1.19 (gritter) 1/22/06";
37 #else
38 static const char sccsid[] USED = "@(#)who.sl	1.19 (gritter) 1/22/06";
39 #endif
40 
41 #include	<sys/types.h>
42 #include	<sys/stat.h>
43 #include	<sys/time.h>
44 #include	<fcntl.h>
45 #include	<unistd.h>
46 #include	<stdio.h>
47 #include	<string.h>
48 #include	<stdlib.h>
49 #include	<errno.h>
50 #include	<time.h>
51 #include	<libgen.h>
52 #include	<utmpx.h>
53 #include	<limits.h>
54 
55 enum okay {
56 	OKAY,
57 	STOP
58 };
59 
60 static enum {
61 	FL_b = 00001,
62 	FL_d = 00002,
63 	FL_l = 00004,
64 	FL_p = 00010,
65 	FL_r = 00020,
66 	FL_t = 00040,
67 	FL_A = 00100,
68 	FL_USER = 01000
69 } flags;
70 
71 static unsigned	errcnt;			/* count of errors */
72 static int	Hflag;			/* print headers */
73 static int	mflag;			/* select who am i */
74 static int	qflag;			/* quick who */
75 static int	Rflag;			/* display host name */
76 static int	sflag;			/* short listing */
77 static int	Tflag;			/* show terminal state */
78 static int	uflag;			/* write idle time */
79 static int	number = 8;		/* for -q */
80 static time_t	now;
81 static time_t	boottime;
82 static char	*whoami;		/* current terminal only */
83 static char	*progname;		/* argv[0] to main() */
84 
85 static void
usage(void)86 usage(void)
87 {
88 	fprintf(stderr, "\n\
89 Usage:\t%s[-abdHlmnpqrRstTu] [am i] [utmp_like_file]\n\
90 a\tAll options (same as -bdlprtTu)\n\
91 b\tboot record\n\
92 d\tdead processes\n\
93 H\tprint column headings\n\
94 l\tlogin lines\n\
95 m\trestrict to current terminal\n\
96 n #\tnumber of columns for -q option\n\
97 p\tother active processes\n\
98 q\tquick format\n\
99 r\trun-level record\n\
100 R\tprint host name\n\
101 s\tshort who (default)\n\
102 t\tsystem time changes\n\
103 T\tterminal status (+ writable, - not writable, ? indeterminable)\n\
104 u\tadds idle time\n\
105 ",
106 		progname);
107 	exit(2);
108 }
109 
110 static void
header(void)111 header(void)
112 {
113 	printf("NAME       LINE         TIME");
114 	if (uflag)
115 		printf("          IDLE    PID");
116 	if (flags & (FL_d|FL_r))
117 		printf("%s COMMENTS", uflag ? " " : "         ");
118 	putchar('\n');
119 }
120 
121 static void
print(const struct utmpx * u)122 print(const struct utmpx *u)
123 {
124 	struct tm	*tp;
125 	char	buf[LINE_MAX];
126 	const char	*cp;
127 	int	c;
128 	time_t	t;
129 
130 	if (u->ut_type == LOGIN_PROCESS)
131 		cp = "LOGIN";
132 #if defined(__FreeBSD__) && __FreeBSD_version >= 900007
133 	else if (u->ut_type == BOOT_TIME || u->ut_user[0] == '\0')
134 #else
135 	else if (u->ut_type == BOOT_TIME || u->ut_type == RUN_LVL || u->ut_user[0] == '\0')
136 #endif
137 		cp = "   .";
138 	else
139 		cp = u->ut_user;
140 	printf("%-8.*s ", (int)sizeof u->ut_user, cp);
141 	if (Tflag && u->ut_type == USER_PROCESS) {
142 		struct stat	st;
143 
144 		snprintf(buf, sizeof buf, "/dev/%.*s",
145 				(int)sizeof u->ut_line, u->ut_line);
146 		if (stat(buf, &st) == 0)
147 			c = (st.st_mode & 0022) ? '+' : '-';
148 		else
149 			c = '?';
150 	} else
151 		c = ' ';
152 	printf("%c ", c);
153 
154 #if !(defined(__FreeBSD__) && __FreeBSD_version >= 900007)
155 	if (u->ut_type == RUN_LVL) {
156 		snprintf(buf, sizeof buf, "run-level %c",
157 				(int)(u->ut_pid & 0377));
158 		cp = buf;
159 	} else
160 
161 #endif
162 	    if (u->ut_type == BOOT_TIME)
163 		cp = "system boot";
164 	else if (u->ut_line[0] == '\0')
165 		cp = "     .";
166 	else
167 		cp = u->ut_line;
168 	printf("%-12.*s ", (int)sizeof u->ut_line, cp);
169 	t = u->ut_tv.tv_sec;
170 	tp = localtime(&t);
171 	strftime(buf, sizeof buf, "%b %e %H:%M", tp);
172 	printf("%s", buf);
173 	if (uflag && !sflag) {
174 		struct stat	st;
175 
176 		snprintf(buf, sizeof buf, "/dev/%.*s",
177 				(int)sizeof u->ut_line, u->ut_line);
178 		if (stat(buf, &st) == 0) {
179 			time_t	idle;
180 			int	hours, minutes;
181 
182 			if ((idle = now - st.st_mtime) < 0)
183 				idle = 0;
184 			hours = idle / 3600;
185 			minutes = (idle % 3600) / 60;
186 			/*
187 			 * Boottime might not be particularly accurate;
188 			 * add 10 seconds grace time.
189 			 */
190 			if (hours >= 24 || st.st_mtime <= boottime + 10)
191 				cp = " old ";
192 			else if (hours > 0 || minutes > 0) {
193 				snprintf(buf, sizeof buf, "%2d:%02d",
194 						hours, minutes);
195 				cp = buf;
196 			} else
197 				cp = "  .  ";
198 			printf(" %s", cp);
199 		}
200 		if (
201 #if !(defined(__FreeBSD__) && __FreeBSD_version >= 900007)
202 			u->ut_type != RUN_LVL &&
203 #endif
204 			u->ut_type != BOOT_TIME
205 #ifdef	ACCOUNTING
206 				&& u->ut_type != ACCOUNTING
207 #endif	/* ACCOUNTING */
208 				)
209 			printf(" %6d", (int)u->ut_pid);
210 	}
211 	if (u->ut_type == DEAD_PROCESS && !sflag)
212 #ifdef	__hpux
213 #define	e_termination	__e_termination
214 #define	e_exit		__e_exit
215 #endif	/* __hpux */
216 		printf("  id=%4.4s term=%-3d exit=%d",
217 				u->ut_id,
218 #if (!defined (_AIX) || !defined (__APPLE__)) && !defined(__FreeBSD__)
219 				u->ut_exit.e_termination,
220 				u->ut_exit.e_exit
221 #else	/* _AIX, __APPLE__ */
222 				0,
223 				0
224 #endif	/* _AIX, __APPLE__ */
225 				);
226 	else if (u->ut_type == INIT_PROCESS && !sflag)
227 		printf("  id=%4.4s", u->ut_id);
228 
229 #if !(defined(__FreeBSD__) && __FreeBSD_version >= 900007)
230 	else if (u->ut_type == RUN_LVL)
231 		printf("    %c    %-4ld %c", (int)(u->ut_pid & 0377),
232 				0L, (int)((u->ut_pid & 0177777) / 0400));
233 #endif
234 	if (Rflag && u->ut_host[0])
235 		printf("\t(%.*s)", (int)sizeof u->ut_host, u->ut_host);
236 	putchar('\n');
237 }
238 
239 static enum okay
selected(const struct utmpx * u)240 selected(const struct utmpx *u)
241 {
242 	enum okay	val = STOP;
243 
244 	switch (u->ut_type) {
245 
246 #if !(defined(__FreeBSD__) && __FreeBSD_version >= 900007)
247 	case RUN_LVL:
248 		if (flags & FL_r)
249 			val = OKAY;
250 		break;
251 #endif
252 	case BOOT_TIME:
253 		if (flags & FL_b)
254 			val = OKAY;
255 		break;
256 	case NEW_TIME:
257 	case OLD_TIME:
258 		if (flags & FL_t)
259 			val = OKAY;
260 		break;
261 	case INIT_PROCESS:
262 		if (flags & FL_p)
263 			val = OKAY;
264 		break;
265 	case LOGIN_PROCESS:
266 		if (flags & FL_l)
267 			val = OKAY;
268 		break;
269 	case USER_PROCESS:
270 		if (flags & FL_USER)
271 			val = OKAY;
272 		break;
273 	case DEAD_PROCESS:
274 		if (flags & FL_d)
275 			val = OKAY;
276 		break;
277 #ifdef	ACCOUNTING
278 	case ACCOUNTING:
279 		if (flags & FL_A)
280 			val = OKAY;
281 #endif
282 	}
283 	if (val == OKAY && whoami != NULL && strncmp(whoami, u->ut_line,
284 				sizeof u->ut_line))
285 		val = STOP;
286 	return val;
287 }
288 
289 static void
who(void)290 who(void)
291 {
292 	struct utmpx	*u;
293 	int	users = 0, printed = 0;
294 
295 	if (Hflag)
296 		header();
297 	if (uflag) {
298 		struct utmpx	id;
299 		setutxent();
300 		id.ut_type = BOOT_TIME;
301 		if ((u = getutxid(&id)) != NULL)
302 			boottime = u->ut_tv.tv_sec;
303 	}
304 	setutxent();
305 	time(&now);
306 	while ((u = getutxent()) != NULL) {
307 		if (qflag) {
308 			if (u->ut_type == USER_PROCESS) {
309 				users++;
310 				printf("%-8.*s%c", (int)sizeof u->ut_user,
311 						u->ut_user,
312 						++printed < number ?
313 						' ' : '\n');
314 				if (printed == number)
315 					printed = 0;
316 			}
317 		} else {
318 			if (selected(u) == OKAY)
319 				print(u);
320 		}
321 	}
322 	endutxent();
323 	if (qflag) {
324 		if (printed > 0)
325 			putchar('\n');
326 		printf("# users=%u\n", users);
327 	}
328 }
329 
330 int
main(int argc,char ** argv)331 main(int argc, char **argv)
332 {
333 	const char	optstring[] = "AabdHlmn:pqRrstTu";
334 	int	i;
335 
336 #ifdef	__GLIBC__
337 	putenv("POSIXLY_CORRECT=1");
338 #endif
339 	progname = basename(argv[0]);
340 	while ((i = getopt(argc, argv, optstring)) != EOF) {
341 		switch (i) {
342 		case 'A':
343 			flags |= FL_A;
344 			break;
345 		case 'a':
346 			flags |= FL_b|FL_d|FL_l|FL_p|FL_r|FL_t|FL_USER;
347 			uflag = Tflag = 1;
348 			break;
349 		case 'b':
350 			flags |= FL_b;
351 			break;
352 		case 'd':
353 			flags |= FL_d;
354 			break;
355 		case 'H':
356 			Hflag = 1;
357 			break;
358 		case 'l':
359 			flags |= FL_l;
360 			break;
361 		case 'm':
362 			mflag = 1;
363 			break;
364 		case 'n':
365 			if ((number = atoi(optarg)) < 1)
366 				number = 1;
367 			break;
368 		case 'p':
369 			flags |= FL_p;
370 			break;
371 		case 'q':
372 			qflag = 1;
373 			break;
374 		case 'R':
375 			Rflag = 1;
376 			break;
377 		case 'r':
378 			flags |= FL_r;
379 			break;
380 		case 's':
381 			sflag = 1;
382 			break;
383 		case 't':
384 			flags |= FL_t;
385 			break;
386 		case 'T':
387 			Tflag = 1;
388 #ifdef	SUS
389 			break;
390 #else
391 			/*FALLTHRU*/
392 #endif
393 		case 'u':
394 			uflag = 1;
395 			break;
396 		default:
397 			usage();
398 		}
399 	}
400 	if (flags == 0)
401 		flags |= FL_USER;
402 	if (argc - optind == 1) {
403 		struct stat	st;
404 		int	fd;
405 
406 		if (stat(argv[optind], &st) < 0) {
407 			fprintf(stderr, "%s: Cannot stat file '%s': %s\n",
408 					progname,
409 					argv[optind], strerror(errno));
410 			exit(1);
411 		}
412 		if ((fd = open(argv[optind], O_RDONLY)) < 0) {
413 			fprintf(stderr, "%s: Cannot open file '%s': %s\n",
414 					progname,
415 					argv[optind], strerror(errno));
416 			exit(1);
417 		}
418 		close(fd);
419 		if (st.st_size % sizeof (struct utmpx)) {
420 			fprintf(stderr, "%s: File '%s' is not a utmp file\n",
421 					progname, argv[optind]);
422 			exit(1);
423 		}
424 #if !defined (__hpux) && !defined (_AIX)
425 		utmpxname(argv[optind]);
426 #else	/* __hpux, _AIX */
427 		fprintf(stderr, "%s: %s\n", progname, strerror(errno));
428 		exit(1);
429 #endif	/* __hpux, _AIX */
430 	}
431 	if (mflag || (argc - optind == 2 && strcmp(argv[optind], "am") == 0 &&
432 			(argv[optind+1][0] == 'i' ||
433 			 argv[optind+1][0] == 'I') &&
434 			argv[optind+1][1] == '\0')) {
435 		if ((whoami = ttyname(0)) == NULL &&
436 				(whoami = ttyname(1)) == NULL &&
437 				(whoami = ttyname(2)) == NULL) {
438 			fprintf(stderr, "Must be attached to a terminal "
439 					"for the '%s' option\n",
440 					mflag ? "-m" : "am I");
441 			exit(4);
442 		}
443 		if (strncmp(whoami, "/dev/", 5) == 0)
444 			whoami = &whoami[5];
445 	}
446 	who();
447 	return errcnt;
448 }
449