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