]>
Commit | Line | Data |
---|---|---|
ddb4a88b A |
1 | /* $NetBSD: who.c,v 1.23 2008/07/24 15:35:41 christos Exp $ */ |
2 | ||
3 | /* | |
4 | * Copyright (c) 1989, 1993 | |
5 | * The Regents of the University of California. All rights reserved. | |
6 | * | |
7 | * This code is derived from software contributed to Berkeley by | |
8 | * Michael Fischbein. | |
44bd5ea7 A |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
ddb4a88b A |
18 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | |
44bd5ea7 | 21 | * |
ddb4a88b | 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
44bd5ea7 A |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
ddb4a88b | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
44bd5ea7 A |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include <sys/cdefs.h> | |
ddb4a88b A |
36 | #ifndef lint |
37 | __COPYRIGHT("@(#) Copyright (c) 1989, 1993\ | |
38 | The Regents of the University of California. All rights reserved."); | |
39 | #endif /* not lint */ | |
40 | ||
41 | #ifndef lint | |
42 | #if 0 | |
43 | static char sccsid[] = "@(#)who.c 8.1 (Berkeley) 6/6/93"; | |
44 | #endif | |
45 | __RCSID("$NetBSD: who.c,v 1.23 2008/07/24 15:35:41 christos Exp $"); | |
46 | #endif /* not lint */ | |
44bd5ea7 A |
47 | |
48 | #include <sys/types.h> | |
49 | #include <sys/stat.h> | |
1c4c78a5 | 50 | |
44bd5ea7 A |
51 | #include <err.h> |
52 | #include <locale.h> | |
53 | #include <pwd.h> | |
54 | #include <stdio.h> | |
55 | #include <stdlib.h> | |
56 | #include <string.h> | |
57 | #include <time.h> | |
58 | #include <unistd.h> | |
ddb4a88b A |
59 | #ifdef SUPPORT_UTMP |
60 | #include <utmp.h> | |
61 | #endif | |
62 | #ifdef SUPPORT_UTMPX | |
1a5bac72 | 63 | #include <utmpx.h> |
ddb4a88b A |
64 | #endif |
65 | #ifdef __APPLE__ | |
66 | #include <limits.h> | |
67 | #include <paths.h> | |
68 | #include <stdint.h> | |
69 | #endif /* __APPLE__ */ | |
70 | ||
71 | #include "utmpentry.h" | |
44bd5ea7 | 72 | |
1a5bac72 | 73 | #ifdef __APPLE__ |
ddb4a88b | 74 | #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) |
1a5bac72 | 75 | #endif /* __APPLE__ */ |
ddb4a88b A |
76 | |
77 | static void output_labels(void); | |
78 | static void who_am_i(const char *, int); | |
79 | static void usage(void) __dead; | |
80 | static void process(const char *, int); | |
81 | static void eprint(const struct utmpentry *); | |
82 | static void print(const char *, const char *, time_t, const char *, pid_t pid, | |
83 | uint16_t term, uint16_t xit, uint16_t sess, uint16_t type); | |
84 | static void quick(const char *); | |
85 | ||
86 | static int show_term; /* show term state */ | |
87 | static int show_idle; /* show idle time */ | |
88 | #ifndef __APPLE__ | |
89 | static int show_details; /* show exit status etc. */ | |
90 | #endif /* !__APPLE__ */ | |
91 | ||
92 | struct ut_type_names { | |
93 | int type; | |
94 | const char *name; | |
95 | } ut_type_names[] = { | |
96 | #ifdef SUPPORT_UTMPX | |
97 | { EMPTY, "empty" }, | |
98 | { RUN_LVL, "run level" }, | |
99 | { BOOT_TIME, "boot time" }, | |
100 | { OLD_TIME, "old time" }, | |
101 | { NEW_TIME, "new time" }, | |
102 | { INIT_PROCESS, "init process" }, | |
103 | { LOGIN_PROCESS, "login process" }, | |
104 | { USER_PROCESS, "user process" }, | |
105 | { DEAD_PROCESS, "dead process" }, | |
106 | #if defined(_NETBSD_SOURCE) | |
107 | { ACCOUNTING, "accounting" }, | |
108 | { SIGNATURE, "signature" }, | |
109 | { DOWN_TIME, "down time" }, | |
110 | #endif /* _NETBSD_SOURCE */ | |
111 | #endif /* SUPPORT_UTMPX */ | |
112 | { -1, "unknown" } | |
113 | }; | |
44bd5ea7 A |
114 | |
115 | int | |
1c4c78a5 | 116 | main(int argc, char *argv[]) |
44bd5ea7 | 117 | { |
ddb4a88b A |
118 | int c, only_current_term, show_labels, quick_mode, default_mode; |
119 | int et = 0; | |
1c4c78a5 | 120 | |
ddb4a88b | 121 | setlocale(LC_ALL, ""); |
44bd5ea7 | 122 | |
ddb4a88b A |
123 | only_current_term = show_term = show_idle = show_labels = 0; |
124 | quick_mode = default_mode = 0; | |
1a5bac72 | 125 | |
ddb4a88b A |
126 | while ((c = getopt(argc, argv, "abdHlmpqrsTtuv")) != -1) { |
127 | switch (c) { | |
128 | case 'a': | |
129 | et = -1; | |
130 | #ifdef __APPLE__ | |
131 | show_idle = 1; | |
132 | #else /* !__APPLE__ */ | |
133 | show_idle = show_details = 1; | |
134 | #endif /* __APPLE__ */ | |
1c4c78a5 | 135 | break; |
ddb4a88b A |
136 | case 'b': |
137 | et |= (1 << BOOT_TIME); | |
1c4c78a5 | 138 | break; |
ddb4a88b A |
139 | case 'd': |
140 | et |= (1 << DEAD_PROCESS); | |
1c4c78a5 | 141 | break; |
ddb4a88b A |
142 | case 'H': |
143 | show_labels = 1; | |
1c4c78a5 | 144 | break; |
ddb4a88b A |
145 | case 'l': |
146 | et |= (1 << LOGIN_PROCESS); | |
1c4c78a5 | 147 | break; |
ddb4a88b A |
148 | case 'm': |
149 | only_current_term = 1; | |
1c4c78a5 | 150 | break; |
ddb4a88b A |
151 | case 'p': |
152 | et |= (1 << INIT_PROCESS); | |
1c4c78a5 | 153 | break; |
ddb4a88b A |
154 | case 'q': |
155 | quick_mode = 1; | |
44bd5ea7 | 156 | break; |
ddb4a88b A |
157 | case 'r': |
158 | et |= (1 << RUN_LVL); | |
44bd5ea7 | 159 | break; |
ddb4a88b A |
160 | case 's': |
161 | default_mode = 1; | |
44bd5ea7 | 162 | break; |
ddb4a88b A |
163 | case 'T': |
164 | show_term = 1; | |
1c4c78a5 | 165 | break; |
ddb4a88b A |
166 | case 't': |
167 | et |= (1 << NEW_TIME); | |
1c4c78a5 | 168 | break; |
ddb4a88b A |
169 | case 'u': |
170 | show_idle = 1; | |
44bd5ea7 | 171 | break; |
ddb4a88b A |
172 | #ifndef __APPLE__ |
173 | case 'v': | |
174 | show_details = 1; | |
175 | break; | |
176 | #endif /* !__APPLE__ */ | |
44bd5ea7 A |
177 | default: |
178 | usage(); | |
ddb4a88b | 179 | /* NOTREACHED */ |
44bd5ea7 A |
180 | } |
181 | } | |
182 | argc -= optind; | |
183 | argv += optind; | |
184 | ||
ddb4a88b A |
185 | if (et != 0) |
186 | etype = et; | |
187 | ||
188 | #ifndef __APPLE__ | |
189 | if (chdir("/dev")) { | |
190 | err(EXIT_FAILURE, "cannot change directory to /dev"); | |
191 | /* NOTREACHED */ | |
44bd5ea7 | 192 | } |
ddb4a88b A |
193 | #endif /* !__APPLE__ */ |
194 | ||
195 | if (default_mode) | |
196 | only_current_term = show_term = show_idle = 0; | |
197 | ||
198 | switch (argc) { | |
199 | case 0: /* who */ | |
200 | if (quick_mode) { | |
201 | quick(NULL); | |
202 | } else if (only_current_term) { | |
203 | who_am_i(NULL, show_labels); | |
204 | } else { | |
205 | process(NULL, show_labels); | |
206 | } | |
207 | break; | |
208 | case 1: /* who utmp_file */ | |
209 | if (quick_mode) { | |
210 | quick(*argv); | |
211 | } else if (only_current_term) { | |
212 | who_am_i(*argv, show_labels); | |
213 | } else { | |
214 | process(*argv, show_labels); | |
215 | } | |
216 | break; | |
217 | case 2: /* who am i */ | |
218 | who_am_i(NULL, show_labels); | |
219 | break; | |
220 | default: | |
1c4c78a5 | 221 | usage(); |
ddb4a88b | 222 | /* NOTREACHED */ |
e1a085ba | 223 | } |
1c4c78a5 | 224 | |
ddb4a88b A |
225 | return 0; |
226 | } | |
44bd5ea7 | 227 | |
ddb4a88b A |
228 | static char * |
229 | strrstr(const char *str, const char *pat) | |
230 | { | |
231 | const char *estr; | |
232 | size_t len; | |
233 | if (*pat == '\0') | |
234 | return __UNCONST(str); | |
1c4c78a5 | 235 | |
ddb4a88b | 236 | len = strlen(pat); |
1c4c78a5 | 237 | |
ddb4a88b A |
238 | for (estr = str + strlen(str); str < estr; estr--) |
239 | if (strncmp(estr, pat, len) == 0) | |
240 | return __UNCONST(estr); | |
241 | return NULL; | |
44bd5ea7 A |
242 | } |
243 | ||
1c4c78a5 | 244 | static void |
ddb4a88b | 245 | who_am_i(const char *fname, int show_labels) |
44bd5ea7 | 246 | { |
ddb4a88b A |
247 | struct passwd *pw; |
248 | const char *p; | |
249 | char *t; | |
250 | time_t now; | |
251 | struct utmpentry *ehead, *ep; | |
252 | ||
253 | /* search through the utmp and find an entry for this tty */ | |
254 | if ((p = ttyname(STDIN_FILENO)) != NULL) { | |
255 | ||
256 | /* strip directory prefixes for ttys */ | |
257 | if ((t = strrstr(p, "/pts/")) != NULL || | |
258 | (t = strrchr(p, '/')) != NULL) | |
259 | p = t + 1; | |
260 | ||
261 | (void)getutentries(fname, &ehead); | |
262 | for (ep = ehead; ep; ep = ep->next) | |
263 | if (strcmp(ep->line, p) == 0) { | |
264 | if (show_labels) | |
265 | output_labels(); | |
266 | eprint(ep); | |
267 | return; | |
268 | } | |
269 | } else | |
270 | p = "tty??"; | |
271 | ||
272 | (void)time(&now); | |
273 | pw = getpwuid(getuid()); | |
274 | if (show_labels) | |
275 | output_labels(); | |
276 | print(pw ? pw->pw_name : "?", p, now, "", getpid(), 0, 0, 0, 0); | |
44bd5ea7 A |
277 | } |
278 | ||
1c4c78a5 | 279 | static void |
ddb4a88b | 280 | process(const char *fname, int show_labels) |
44bd5ea7 | 281 | { |
ddb4a88b A |
282 | struct utmpentry *ehead, *ep; |
283 | (void)getutentries(fname, &ehead); | |
284 | if (show_labels) | |
285 | output_labels(); | |
286 | for (ep = ehead; ep != NULL; ep = ep->next) | |
287 | eprint(ep); | |
288 | #ifdef __APPLE__ | |
289 | if ((etype & (1 << RUN_LVL)) != 0) { | |
290 | printf(" . run-level 3\n"); | |
291 | } | |
292 | #endif /* __APPLE__ */ | |
293 | } | |
1c4c78a5 | 294 | |
ddb4a88b A |
295 | static void |
296 | eprint(const struct utmpentry *ep) | |
297 | { | |
298 | print(ep->name, ep->line, (time_t)ep->tv.tv_sec, ep->host, ep->pid, | |
299 | #ifdef __APPLE__ | |
300 | 0, 0, 0, ep->type); | |
301 | #else /* !__APPLE__ */ | |
302 | ep->term, ep->exit, ep->sess, ep->type); | |
303 | #endif /* __APPLE__ */ | |
1c4c78a5 A |
304 | } |
305 | ||
306 | static void | |
ddb4a88b A |
307 | print(const char *name, const char *line, time_t t, const char *host, |
308 | pid_t pid, uint16_t term, uint16_t xit, uint16_t sess, uint16_t type) | |
1c4c78a5 | 309 | { |
44bd5ea7 | 310 | struct stat sb; |
44bd5ea7 | 311 | char state; |
ddb4a88b A |
312 | static time_t now = 0; |
313 | time_t idle; | |
314 | const char *types = NULL; | |
315 | size_t i; | |
44bd5ea7 | 316 | |
ddb4a88b A |
317 | state = '?'; |
318 | idle = 0; | |
1c4c78a5 | 319 | |
ddb4a88b A |
320 | for (i = 0; ut_type_names[i].type >= 0; i++) { |
321 | types = ut_type_names[i].name; | |
322 | if (ut_type_names[i].type == type) | |
323 | break; | |
324 | } | |
325 | ||
326 | if (show_term || show_idle) { | |
327 | if (now == 0) | |
328 | time(&now); | |
329 | ||
330 | #ifdef __APPLE__ | |
331 | char tty[PATH_MAX + 1]; | |
332 | snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, line); | |
1c4c78a5 | 333 | if (stat(tty, &sb) == 0) { |
ddb4a88b A |
334 | #else /* !__APPLE__ */ |
335 | if (stat(line, &sb) == 0) { | |
336 | #endif /* __APPLE__ */ | |
337 | state = (sb.st_mode & 020) ? '+' : '-'; | |
338 | idle = now - sb.st_atime; | |
1a5bac72 | 339 | } |
ddb4a88b | 340 | |
44bd5ea7 A |
341 | } |
342 | ||
ddb4a88b A |
343 | #ifdef __APPLE__ |
344 | switch (type) { | |
345 | case LOGIN_PROCESS: | |
346 | (void)printf("%-*.*s ", maxname, maxname, "LOGIN"); | |
347 | break; | |
348 | case BOOT_TIME: | |
349 | (void)printf("%-*.*s ", maxname, maxname, "reboot"); | |
350 | break; | |
351 | default: | |
352 | (void)printf("%-*.*s ", maxname, maxname, name); | |
353 | break; | |
44bd5ea7 | 354 | } |
ddb4a88b A |
355 | #else /* !__APPLE__ */ |
356 | (void)printf("%-*.*s ", maxname, maxname, name); | |
357 | #endif /* __APPLE__ */ | |
44bd5ea7 | 358 | |
ddb4a88b A |
359 | if (show_term) |
360 | (void)printf("%c ", state); | |
44bd5ea7 | 361 | |
ddb4a88b A |
362 | #ifdef __APPLE__ |
363 | (void)printf("%-*.*s ", maxline, maxline, type == BOOT_TIME ? "~" : line); | |
364 | #else /* !__APPLE__ */ | |
365 | (void)printf("%-*.*s ", maxline, maxline, line); | |
366 | #endif /* __APPLE__ */ | |
367 | (void)printf("%.12s ", ctime(&t) + 4); | |
368 | ||
369 | if (show_idle) { | |
370 | if (idle < 60) | |
371 | (void)printf(" . "); | |
372 | else if (idle < (24 * 60 * 60)) | |
373 | (void)printf("%02ld:%02ld ", | |
374 | (long)(idle / (60 * 60)), | |
375 | (long)(idle % (60 * 60)) / 60); | |
376 | else | |
377 | (void)printf(" old "); | |
378 | ||
379 | (void)printf("\t%6d", pid); | |
380 | ||
381 | #ifndef __APPLE__ | |
382 | if (show_details) { | |
383 | if (type == RUN_LVL) | |
384 | (void)printf("\tnew=%c old=%c", term, xit); | |
385 | else | |
386 | (void)printf("\tterm=%d exit=%d", term, xit); | |
387 | (void)printf(" sess=%d", sess); | |
388 | (void)printf(" type=%s ", types); | |
e1a085ba | 389 | } |
ddb4a88b | 390 | #endif /* !__APPLE__ */ |
e1a085ba | 391 | } |
ddb4a88b A |
392 | |
393 | #ifdef __APPLE__ | |
394 | /* 6179576 */ | |
395 | if (type == DEAD_PROCESS) | |
396 | (void)printf("\tterm=%d exit=%d", 0, 0); | |
397 | #endif /* __APPLE__ */ | |
1c4c78a5 | 398 | |
ddb4a88b A |
399 | if (*host) |
400 | (void)printf("\t(%.*s)", maxhost, host); | |
401 | (void)putchar('\n'); | |
1c4c78a5 A |
402 | } |
403 | ||
404 | static void | |
ddb4a88b | 405 | output_labels(void) |
1c4c78a5 | 406 | { |
ddb4a88b A |
407 | (void)printf("%-*.*s ", maxname, maxname, "USER"); |
408 | ||
409 | if (show_term) | |
410 | (void)printf("S "); | |
411 | ||
412 | (void)printf("%-*.*s ", maxline, maxline, "LINE"); | |
413 | (void)printf("WHEN "); | |
414 | ||
415 | if (show_idle) { | |
416 | (void)printf("IDLE "); | |
417 | (void)printf("\t PID"); | |
418 | ||
419 | (void)printf("\tCOMMENT"); | |
420 | } | |
421 | ||
422 | (void)putchar('\n'); | |
44bd5ea7 A |
423 | } |
424 | ||
1c4c78a5 | 425 | static void |
ddb4a88b | 426 | quick(const char *fname) |
44bd5ea7 | 427 | { |
ddb4a88b A |
428 | struct utmpentry *ehead, *ep; |
429 | int num = 0; | |
430 | ||
431 | (void)getutentries(fname, &ehead); | |
432 | for (ep = ehead; ep != NULL; ep = ep->next) { | |
433 | (void)printf("%-*s ", maxname, ep->name); | |
434 | if ((++num % 8) == 0) | |
435 | (void)putchar('\n'); | |
e1a085ba | 436 | } |
ddb4a88b A |
437 | if (num % 8) |
438 | (void)putchar('\n'); | |
44bd5ea7 | 439 | |
ddb4a88b | 440 | (void)printf("# users = %d\n", num); |
44bd5ea7 A |
441 | } |
442 | ||
ddb4a88b A |
443 | static void |
444 | usage(void) | |
44bd5ea7 | 445 | { |
ddb4a88b A |
446 | #ifdef __APPLE__ |
447 | (void)fprintf(stderr, "Usage: %s [-abdHlmpqrsTtu] [file]\n\t%s am i\n", | |
448 | #else /* !__APPLE__ */ | |
449 | (void)fprintf(stderr, "Usage: %s [-abdHlmqrsTtuv] [file]\n\t%s am i\n", | |
450 | #endif /* __APPLE__ */ | |
451 | getprogname(), getprogname()); | |
452 | exit(EXIT_FAILURE); | |
44bd5ea7 | 453 | } |