]> git.saurik.com Git - apple/shell_cmds.git/blob - who/who.c
b700264473e9aa4ef5685e1f073c590589a47d26
[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 <utmp.h>
50 #include <utmpx.h>
51
52 static void heading(void);
53 static void process_utmp(FILE *);
54 static void process_wtmp(FILE *);
55 static void quick(FILE *);
56 static void row(struct utmp *);
57 static int ttywidth(void);
58 static void usage(void);
59 static void whoami(FILE *);
60
61 static int bflag; /* date & time of last reboot */
62 static int dflag; /* dead processes */
63 static int Hflag; /* Write column headings */
64 static int lflag; /* waiting to login */
65 static int mflag; /* Show info about current terminal */
66 static int pflag; /* Processes active & spawned by init */
67 static int qflag; /* "Quick" mode */
68 static int rflag; /* run-level of the init process */
69 static int sflag; /* Show name, line, time */
70 static int tflag; /* time of change to system clock */
71 static int Tflag; /* Show terminal state */
72 static int uflag; /* Show idle time */
73 #ifdef __APPLE__
74 #include <get_compat.h>
75 #else /* !__APPLE__ */
76 #define COMPAT_MODE(a,b) (1)
77 #endif /* __APPLE__ */
78 static int unix2003_std;
79 static struct utmpx *utx_db = NULL;
80
81 int
82 main(int argc, char *argv[])
83 {
84 int ch;
85 const char *file;
86 FILE *fp;
87 FILE *wtmp_fp;
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 file = *argv;
156 else
157 file = _PATH_UTMP;
158 if ((fp = fopen(file, "r")) == NULL)
159 err(1, "%s", file);
160
161 if (qflag)
162 quick(fp);
163 else {
164 if (sflag)
165 Tflag = uflag = 0;
166 if (Hflag)
167 heading();
168 if (mflag)
169 whoami(fp);
170 else
171 /* read and process utmp file for relevant options */
172 if( Tflag || uflag || !(bflag || dflag || lflag || pflag || rflag) )
173 process_utmp(fp);
174 }
175
176 fclose(fp);
177
178 /* read and process wtmp file for relevant options */
179 if (bflag || dflag || lflag || pflag || rflag ) {
180
181 /* Open the wtmp file */
182 if ((wtmp_fp = fopen(_PATH_WTMP, "r")) == NULL)
183 err(1, "%s", _PATH_WTMP);
184 else {
185 process_wtmp(wtmp_fp);
186 fclose(wtmp_fp);
187 }
188 }
189
190 if (utx_db) {
191 endutxent(); /* close db */
192 }
193 exit(0);
194 }
195
196 static void
197 usage(void)
198 {
199
200 fprintf(stderr, "usage: who [-abdHlmpqrstTu] [am I] [file]\n");
201 exit(1);
202 }
203
204 static void
205 heading(void)
206 {
207
208 printf("%-*s ", UT_NAMESIZE, "NAME");
209 if (Tflag)
210 printf("S ");
211 printf("%-*s ", UT_LINESIZE, "LINE");
212 printf("%-*s ", 12, "TIME");
213 if (uflag)
214 printf("IDLE ");
215 if (unix2003_std && uflag && !Tflag)
216 printf(" PID ");
217 printf("%-*s", UT_HOSTSIZE, "FROM");
218 putchar('\n');
219 }
220
221 static void
222 row(struct utmp *ut)
223 {
224 char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE];
225 struct stat sb;
226 time_t idle, t;
227 static int d_first = -1;
228 struct tm *tm;
229 char state;
230 char login_pidstr[20];
231
232 if (d_first < 0)
233 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
234
235 if (Tflag || uflag) {
236 snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV,
237 UT_LINESIZE, ut->ut_line);
238 state = '?';
239 idle = 0;
240 if (stat(tty, &sb) == 0) {
241 state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
242 '+' : '-';
243 idle = time(NULL) - sb.st_mtime;
244 }
245 if (unix2003_std && !Tflag) {
246 /* uflag without Tflag */
247 struct utmpx * utx = NULL;
248 if (!utx_db) {
249 utx_db = getutxent(); /* just to open db */
250 }
251 if (utx_db) {
252 struct utmpx this_line;
253 setutxent(); /* reset db */
254 memset(&this_line, 0, sizeof(this_line));
255 /*
256 strcpy(this_line.ut_user, ut->ut_name);
257 */
258 strcpy(this_line.ut_line, ut->ut_line);
259 utx = getutxline(&this_line);
260 }
261 if (utx) {
262 snprintf(login_pidstr,sizeof(login_pidstr),
263 "%8d",utx->ut_pid);
264 } else {
265 strcpy(login_pidstr," ?");
266 }
267 }
268 }
269
270 printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name);
271 if (Tflag)
272 printf("%c ", state);
273 printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line);
274 t = _time32_to_time(ut->ut_time);
275 tm = localtime(&t);
276 strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
277 printf("%-*s ", 12, buf);
278 if (uflag) {
279 if (idle < 60)
280 printf(" . ");
281 else if (idle < 24 * 60 * 60)
282 printf("%02d:%02d ", (int)(idle / 60 / 60),
283 (int)(idle / 60 % 60));
284 else
285 printf(" old ");
286 if (unix2003_std && !Tflag) {
287 printf("%s ", login_pidstr);
288 }
289 }
290 if (*ut->ut_host != '\0')
291 printf("(%.*s)", UT_HOSTSIZE, ut->ut_host);
292 putchar('\n');
293
294 }
295
296 static void
297 process_utmp(FILE *fp)
298 {
299 struct utmp ut;
300
301 while (fread(&ut, sizeof(ut), 1, fp) == 1)
302 if (*ut.ut_name != '\0') {
303 row(&ut);
304 }
305 }
306
307 /* For some options, process the wtmp file to generate output */
308 static void
309 process_wtmp(FILE *fp)
310 {
311 struct utmp ut;
312 struct utmp lboot_ut = { "", "", "", 0 };
313 int num = 0; /* count of user entries */
314
315 while (fread(&ut, sizeof(ut), 1, fp) == 1)
316 if (*ut.ut_name != '\0') {
317 if (bflag && (!strcmp(ut.ut_name, "reboot"))) {
318 memcpy(&lboot_ut, &ut, sizeof(ut));
319 }
320 else
321 num++;
322 };
323
324 if (bflag && (!strcmp(lboot_ut.ut_name, "reboot")))
325 row(&lboot_ut);
326
327 /* run level of the init process is unknown in BSD system. If multi
328 user, then display the highest run level. Else, no-op.
329 */
330 if (rflag && (num > 1))
331 printf(" . run-level 3\n");
332 }
333
334 static void
335 quick(FILE *fp)
336 {
337 struct utmp ut;
338 int col, ncols, num;
339
340 ncols = ttywidth();
341 col = num = 0;
342 while (fread(&ut, sizeof(ut), 1, fp) == 1) {
343 if (*ut.ut_name == '\0')
344 continue;
345 printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name);
346 if (++col < ncols / (UT_NAMESIZE + 1))
347 putchar(' ');
348 else {
349 col = 0;
350 putchar('\n');
351 }
352 num++;
353 }
354 if (col != 0)
355 putchar('\n');
356
357 printf("# users = %d\n", num);
358 }
359
360 static void
361 whoami(FILE *fp)
362 {
363 struct utmp ut;
364 struct passwd *pwd;
365 const char *name, *p, *tty;
366
367 if ((tty = ttyname(STDIN_FILENO)) == NULL)
368 tty = "tty??";
369 else if ((p = strrchr(tty, '/')) != NULL)
370 tty = p + 1;
371
372 /* Search utmp for our tty, dump first matching record. */
373 while (fread(&ut, sizeof(ut), 1, fp) == 1)
374 if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty,
375 UT_LINESIZE) == 0) {
376 row(&ut);
377 return;
378 }
379
380 /* Not found; fill the utmp structure with the information we have. */
381 memset(&ut, 0, sizeof(ut));
382 if ((pwd = getpwuid(getuid())) != NULL)
383 name = pwd->pw_name;
384 else
385 name = "?";
386 strncpy(ut.ut_name, name, UT_NAMESIZE);
387 strncpy(ut.ut_line, tty, UT_LINESIZE);
388 ut.ut_time = _time_to_time32(time(NULL));
389 row(&ut);
390 }
391
392 static int
393 ttywidth(void)
394 {
395 struct winsize ws;
396 long width;
397 char *cols, *ep;
398
399 if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
400 errno = 0;
401 width = strtol(cols, &ep, 10);
402 if (errno || width <= 0 || width > INT_MAX || ep == cols ||
403 *ep != '\0')
404 warnx("invalid COLUMNS environment variable ignored");
405 else
406 return (width);
407 }
408 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
409 return (ws.ws_col);
410
411 return (80);
412 }