]> git.saurik.com Git - apple/shell_cmds.git/blob - who/who.c
da46ad77d9e4349d83f99c873ed6fdd6356d1cec
[apple/shell_cmds.git] / who / who.c
1 /*-
2 * Copyright (c) 2002 Tim J. Robbins.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 /* commented as FBSDID not needed for Tiger ......
29 __FBSDID("$FreeBSD: src/usr.bin/who/who.c,v 1.20 2003/10/26 05:05:48 peter Exp $");
30 */
31
32 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <sys/stat.h>
35
36 #include <err.h>
37 #include <errno.h>
38 #include <langinfo.h>
39 #include <limits.h>
40 #include <locale.h>
41 #include <paths.h>
42 #include <pwd.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <time.h>
47 #include <timeconv.h>
48 #include <unistd.h>
49 #include <utmpx.h>
50
51 /* from utmp.h; used only for print formatting */
52 #define UT_NAMESIZE 8
53 #define UT_LINESIZE 8
54 #define UT_HOSTSIZE 16
55
56 static void heading(void);
57 static void process_utmp(void);
58 static void process_wtmp();
59 static void quick(void);
60 static void row(const struct utmpx *);
61 static int ttywidth(void);
62 static void usage(void);
63 static void whoami(void);
64
65 static int bflag; /* date & time of last reboot */
66 static int dflag; /* dead processes */
67 static int Hflag; /* Write column headings */
68 static int lflag; /* waiting to login */
69 static int mflag; /* Show info about current terminal */
70 static int pflag; /* Processes active & spawned by init */
71 static int qflag; /* "Quick" mode */
72 static int rflag; /* run-level of the init process */
73 static int sflag; /* Show name, line, time */
74 static int tflag; /* time of change to system clock */
75 static int Tflag; /* Show terminal state */
76 static int uflag; /* Show idle time */
77 #ifdef __APPLE__
78 #include <get_compat.h>
79 #else /* !__APPLE__ */
80 #define COMPAT_MODE(a,b) (1)
81 #endif /* __APPLE__ */
82 static int unix2003_std;
83
84 int
85 main(int argc, char *argv[])
86 {
87 int ch;
88
89 setlocale(LC_TIME, "");
90
91 unix2003_std = COMPAT_MODE("bin/who", "unix2003");
92
93 while ((ch = getopt(argc, argv, "abdHlmpqrstTu")) != -1) {
94 switch (ch) {
95
96 case 'a': /* -b, -d, -l, -p, -r, -t, -T and -u */
97 bflag = dflag = lflag = pflag = 1;
98 rflag = tflag = Tflag = uflag = 1;
99 break;
100 case 'b': /* date & time of last reboot */
101 bflag = 1;
102 break;
103 case 'd': /* dead processes */
104 dflag = 1;
105 break;
106 case 'H': /* Write column headings */
107 Hflag = 1;
108 break;
109 case 'l': /* waiting to login */
110 lflag = 1;
111 break;
112 case 'm': /* Show info about current terminal */
113 mflag = 1;
114 break;
115 case 'p': /* Processes active & spawned by init */
116 pflag = 1;
117 break;
118 case 'q': /* "Quick" mode */
119 qflag = 1;
120 break;
121 case 'r': /* run-level of the init process */
122 rflag = 1;
123 break;
124 case 's': /* Show name, line, time */
125 sflag = 1;
126 break;
127 case 't': /* time of change to system clock */
128 tflag = 1;
129 break;
130 case 'T': /* Show terminal state */
131 Tflag = 1;
132 break;
133 case 'u': /* Show idle time */
134 uflag = 1;
135 break;
136 default:
137 usage();
138 /*NOTREACHED*/
139 }
140 }
141 argc -= optind;
142 argv += optind;
143
144 if (argc >= 2 && strcmp(argv[0], "am") == 0 &&
145 (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) {
146 /* "who am i" or "who am I", equivalent to -m */
147 mflag = 1;
148 argc -= 2;
149 argv += 2;
150 }
151 if (argc > 1)
152 usage();
153
154 if (*argv != NULL) {
155 if (!utmpxname(*argv) || !wtmpxname(*argv))
156 usage();
157 }
158
159 if (qflag)
160 quick();
161 else {
162 if (sflag)
163 Tflag = uflag = 0;
164 if (Hflag)
165 heading();
166 if (mflag)
167 whoami();
168 else
169 /* read and process utmpx file for relevant options */
170 if( Tflag || uflag || !(bflag || dflag || lflag || pflag || rflag) )
171 process_utmp();
172 }
173
174 /* read and process wtmp file for relevant options */
175 if (bflag || dflag || lflag || pflag || rflag ) {
176
177 process_wtmp();
178 }
179
180 endutxent();
181 exit(0);
182 }
183
184 static void
185 usage(void)
186 {
187
188 fprintf(stderr, "usage: who [-abdHlmpqrstTu] [am I] [file]\n");
189 exit(1);
190 }
191
192 static void
193 heading(void)
194 {
195
196 printf("%-*s ", UT_NAMESIZE, "NAME");
197 if (Tflag)
198 printf("S ");
199 printf("%-*s ", UT_LINESIZE, "LINE");
200 printf("%-*s ", 12, "TIME");
201 if (uflag)
202 printf("IDLE ");
203 if (unix2003_std && uflag && !Tflag)
204 printf(" PID ");
205 printf("%-*s", UT_HOSTSIZE, "FROM");
206 putchar('\n');
207 }
208
209 static void
210 row(const struct utmpx *ut)
211 {
212 char buf[80], tty[sizeof(_PATH_DEV) + _UTX_LINESIZE];
213 struct stat sb;
214 time_t idle, t;
215 static int d_first = -1;
216 struct tm *tm;
217 char state;
218 char login_pidstr[20];
219
220 if (d_first < 0)
221 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
222
223 if (Tflag || uflag) {
224 snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV,
225 _UTX_LINESIZE, ut->ut_line);
226 state = '?';
227 idle = 0;
228 if (stat(tty, &sb) == 0) {
229 state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
230 '+' : '-';
231 idle = time(NULL) - sb.st_mtime;
232 }
233 if (unix2003_std && !Tflag) {
234 /* uflag without Tflag */
235 if (ut->ut_pid) {
236 snprintf(login_pidstr,sizeof(login_pidstr),
237 "%8d",ut->ut_pid);
238 } else {
239 strcpy(login_pidstr," ?");
240 }
241 }
242 }
243
244 printf("%-*.*s ", UT_NAMESIZE, _UTX_USERSIZE, ut->ut_user);
245 if (Tflag)
246 printf("%c ", state);
247 printf("%-*.*s ", UT_LINESIZE, _UTX_LINESIZE, ut->ut_line);
248 t = _time32_to_time(ut->ut_tv.tv_sec);
249 tm = localtime(&t);
250 strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
251 printf("%-*s ", 12, buf);
252 if (uflag) {
253 if (idle < 60)
254 printf(" . ");
255 else if (idle < 24 * 60 * 60)
256 printf("%02d:%02d ", (int)(idle / 60 / 60),
257 (int)(idle / 60 % 60));
258 else
259 printf(" old ");
260 if (unix2003_std && !Tflag) {
261 printf("%s ", login_pidstr);
262 }
263 }
264 if (*ut->ut_host != '\0')
265 printf("(%.*s)", _UTX_HOSTSIZE, ut->ut_host);
266 putchar('\n');
267
268 }
269
270 static void
271 process_utmp(void)
272 {
273 struct utmpx *ut;
274
275 while ((ut = getutxent()) != NULL)
276 if (*ut->ut_user != '\0' && ut->ut_type == USER_PROCESS) {
277 row(ut);
278 }
279 }
280
281 /* For some options, process the wtmp file to generate output */
282 static void
283 process_wtmp(void)
284 {
285 struct utmpx *ut;
286 struct utmpx lboot_ut;
287 int num = 0; /* count of user entries */
288
289 setutxent_wtmp(0); /* zero means reverse chronological */
290 lboot_ut.ut_type = 0;
291 while (!lboot_ut.ut_type && (ut = getutxent_wtmp()) != NULL) {
292 switch(ut->ut_type) {
293 case BOOT_TIME:
294 lboot_ut = *ut;
295 strcpy(lboot_ut.ut_user, "reboot");
296 strcpy(lboot_ut.ut_line, "~");
297 break;
298 case INIT_PROCESS:
299 case LOGIN_PROCESS:
300 case USER_PROCESS:
301 case DEAD_PROCESS:
302 num++;
303 break;
304 }
305 }
306 endutxent_wtmp();
307
308 if (bflag && lboot_ut.ut_type)
309 row(&lboot_ut);
310
311 /* run level of the init process is unknown in BSD system. If multi
312 user, then display the highest run level. Else, no-op.
313 */
314 if (rflag && (num > 1))
315 printf(" . run-level 3\n");
316 }
317
318 static void
319 quick(void)
320 {
321 struct utmpx *ut;
322 int col, ncols, num;
323
324 ncols = ttywidth();
325 col = num = 0;
326 while ((ut = getutxent()) != NULL) {
327 if (*ut->ut_user == '\0' || ut->ut_type != USER_PROCESS)
328 continue;
329 printf("%-*.*s", UT_NAMESIZE, _UTX_USERSIZE, ut->ut_user);
330 if (++col < ncols / (UT_NAMESIZE + 1))
331 putchar(' ');
332 else {
333 col = 0;
334 putchar('\n');
335 }
336 num++;
337 }
338 if (col != 0)
339 putchar('\n');
340
341 printf("# users = %d\n", num);
342 }
343
344 static void
345 whoami(void)
346 {
347 struct utmpx ut;
348 struct utmpx *u;
349 struct passwd *pwd;
350 const char *name, *p, *tty;
351
352 if ((tty = ttyname(STDIN_FILENO)) == NULL)
353 tty = "tty??";
354 else if ((p = strrchr(tty, '/')) != NULL)
355 tty = p + 1;
356
357 memset(&ut, 0, sizeof(ut));
358 strncpy(ut.ut_line, tty, sizeof(ut.ut_line));
359 memcpy(ut.ut_id, tty + (strlen(tty) - sizeof(ut.ut_id)), sizeof(ut.ut_id));
360 ut.ut_type = USER_PROCESS;
361 /* Search utmp for our tty, dump first matching record. */
362 u = getutxid(&ut);
363 if (u) {
364 row(u);
365 return;
366 }
367
368 /* Not found; fill the utmpx structure with the information we have. */
369 if ((pwd = getpwuid(getuid())) != NULL)
370 name = pwd->pw_name;
371 else
372 name = "?";
373 strncpy(ut.ut_user, name, _UTX_USERSIZE);
374 ut.ut_tv.tv_sec = _time_to_time32(time(NULL));
375 row(&ut);
376 }
377
378 static int
379 ttywidth(void)
380 {
381 struct winsize ws;
382 long width;
383 char *cols, *ep;
384
385 if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
386 errno = 0;
387 width = strtol(cols, &ep, 10);
388 if (errno || width <= 0 || width > INT_MAX || ep == cols ||
389 *ep != '\0')
390 warnx("invalid COLUMNS environment variable ignored");
391 else
392 return (width);
393 }
394 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
395 return (ws.ws_col);
396
397 return (80);
398 }