]> git.saurik.com Git - apple/shell_cmds.git/blob - who/who.c
shell_cmds-74.1.1.tar.gz
[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
51 static void heading(void);
52 static void process_utmp(FILE *);
53 static void process_wtmp(FILE *);
54 static void quick(FILE *);
55 static void row(struct utmp *);
56 static int ttywidth(void);
57 static void usage(void);
58 static void whoami(FILE *);
59
60 static int bflag; /* date & time of last reboot */
61 static int dflag; /* dead processes */
62 static int Hflag; /* Write column headings */
63 static int lflag; /* waiting to login */
64 static int mflag; /* Show info about current terminal */
65 static int pflag; /* Processes active & spawned by init */
66 static int qflag; /* "Quick" mode */
67 static int rflag; /* run-level of the init process */
68 static int sflag; /* Show name, line, time */
69 static int tflag; /* time of change to system clock */
70 static int Tflag; /* Show terminal state */
71 static int uflag; /* Show idle time */
72
73 int
74 main(int argc, char *argv[])
75 {
76 int ch;
77 const char *file;
78 FILE *fp;
79 FILE *wtmp_fp;
80
81 setlocale(LC_TIME, "");
82
83 while ((ch = getopt(argc, argv, "abdHlmpqrstTu")) != -1) {
84 switch (ch) {
85
86 case 'a': /* -b, -d, -l, -p, -r, -t, -T and -u */
87 bflag = dflag = lflag = pflag = 1;
88 rflag = tflag = Tflag = uflag = 1;
89 break;
90 case 'b': /* date & time of last reboot */
91 bflag = 1;
92 break;
93 case 'd': /* dead processes */
94 dflag = 1;
95 break;
96 case 'H': /* Write column headings */
97 Hflag = 1;
98 break;
99 case 'l': /* waiting to login */
100 lflag = 1;
101 break;
102 case 'm': /* Show info about current terminal */
103 mflag = 1;
104 break;
105 case 'p': /* Processes active & spawned by init */
106 pflag = 1;
107 break;
108 case 'q': /* "Quick" mode */
109 qflag = 1;
110 break;
111 case 'r': /* run-level of the init process */
112 rflag = 1;
113 break;
114 case 's': /* Show name, line, time */
115 sflag = 1;
116 break;
117 case 't': /* time of change to system clock */
118 tflag = 1;
119 break;
120 case 'T': /* Show terminal state */
121 Tflag = 1;
122 break;
123 case 'u': /* Show idle time */
124 uflag = 1;
125 break;
126 default:
127 usage();
128 /*NOTREACHED*/
129 }
130 }
131 argc -= optind;
132 argv += optind;
133
134 if (argc >= 2 && strcmp(argv[0], "am") == 0 &&
135 (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) {
136 /* "who am i" or "who am I", equivalent to -m */
137 mflag = 1;
138 argc -= 2;
139 argv += 2;
140 }
141 if (argc > 1)
142 usage();
143
144 if (*argv != NULL)
145 file = *argv;
146 else
147 file = _PATH_UTMP;
148 if ((fp = fopen(file, "r")) == NULL)
149 err(1, "%s", file);
150
151 if (qflag)
152 quick(fp);
153 else {
154 if (sflag)
155 Tflag = uflag = 0;
156 if (Hflag)
157 heading();
158 if (mflag)
159 whoami(fp);
160 else
161 /* read and process utmp file for relevant options */
162 if( Tflag || uflag || !(bflag || dflag || lflag || pflag || rflag) )
163 process_utmp(fp);
164 }
165
166 fclose(fp);
167
168 /* read and process wtmp file for relevant options */
169 if (bflag || dflag || lflag || pflag || rflag ) {
170
171 /* Open the wtmp file */
172 if ((wtmp_fp = fopen(_PATH_WTMP, "r")) == NULL)
173 err(1, "%s", _PATH_WTMP);
174 else {
175 process_wtmp(wtmp_fp);
176 fclose(wtmp_fp);
177 }
178 }
179
180 exit(0);
181 }
182
183 static void
184 usage(void)
185 {
186
187 fprintf(stderr, "usage: who [-abdHlmpqrstTu] [am I] [file]\n");
188 exit(1);
189 }
190
191 static void
192 heading(void)
193 {
194
195 printf("%-*s ", UT_NAMESIZE, "NAME");
196 if (Tflag)
197 printf("S ");
198 printf("%-*s ", UT_LINESIZE, "LINE");
199 printf("%-*s ", 12, "TIME");
200 if (uflag)
201 printf("IDLE ");
202 printf("%-*s", UT_HOSTSIZE, "FROM");
203 putchar('\n');
204 }
205
206 static void
207 row(struct utmp *ut)
208 {
209 char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE];
210 struct stat sb;
211 time_t idle, t;
212 static int d_first = -1;
213 struct tm *tm;
214 char state;
215
216 if (d_first < 0)
217 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
218
219 if (Tflag || uflag) {
220 snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV,
221 UT_LINESIZE, ut->ut_line);
222 state = '?';
223 idle = 0;
224 if (stat(tty, &sb) == 0) {
225 state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
226 '+' : '-';
227 idle = time(NULL) - sb.st_mtime;
228 }
229 }
230
231 printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name);
232 if (Tflag)
233 printf("%c ", state);
234 printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line);
235 t = _time32_to_time(ut->ut_time);
236 tm = localtime(&t);
237 strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
238 printf("%-*s ", 12, buf);
239 if (uflag) {
240 if (idle < 60)
241 printf(" . ");
242 else if (idle < 24 * 60 * 60)
243 printf("%02d:%02d ", (int)(idle / 60 / 60),
244 (int)(idle / 60 % 60));
245 else
246 printf(" old ");
247 }
248 if (*ut->ut_host != '\0')
249 printf("(%.*s)", UT_HOSTSIZE, ut->ut_host);
250 putchar('\n');
251
252 }
253
254 static void
255 process_utmp(FILE *fp)
256 {
257 struct utmp ut;
258
259 while (fread(&ut, sizeof(ut), 1, fp) == 1)
260 if (*ut.ut_name != '\0') {
261 row(&ut);
262 }
263 }
264
265 /* For some options, process the wtmp file to generate output */
266 static void
267 process_wtmp(FILE *fp)
268 {
269 struct utmp ut;
270 struct utmp lboot_ut = { "", "", "", 0 };
271 int num = 0; /* count of user entries */
272
273 while (fread(&ut, sizeof(ut), 1, fp) == 1)
274 if (*ut.ut_name != '\0') {
275 if (bflag && (!strcmp(ut.ut_name, "reboot"))) {
276 memcpy(&lboot_ut, &ut, sizeof(ut));
277 }
278 else
279 num++;
280 };
281
282 if (bflag && (!strcmp(lboot_ut.ut_name, "reboot")))
283 row(&lboot_ut);
284
285 /* run level of the init process is unknown in BSD system. If multi
286 user, then display the highest run level. Else, no-op.
287 */
288 if (rflag && (num > 1))
289 printf(" . run-level 3\n");
290 }
291
292 static void
293 quick(FILE *fp)
294 {
295 struct utmp ut;
296 int col, ncols, num;
297
298 ncols = ttywidth();
299 col = num = 0;
300 while (fread(&ut, sizeof(ut), 1, fp) == 1) {
301 if (*ut.ut_name == '\0')
302 continue;
303 printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name);
304 if (++col < ncols / (UT_NAMESIZE + 1))
305 putchar(' ');
306 else {
307 col = 0;
308 putchar('\n');
309 }
310 num++;
311 }
312 if (col != 0)
313 putchar('\n');
314
315 printf("# users = %d\n", num);
316 }
317
318 static void
319 whoami(FILE *fp)
320 {
321 struct utmp ut;
322 struct passwd *pwd;
323 const char *name, *p, *tty;
324
325 if ((tty = ttyname(STDIN_FILENO)) == NULL)
326 tty = "tty??";
327 else if ((p = strrchr(tty, '/')) != NULL)
328 tty = p + 1;
329
330 /* Search utmp for our tty, dump first matching record. */
331 while (fread(&ut, sizeof(ut), 1, fp) == 1)
332 if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty,
333 UT_LINESIZE) == 0) {
334 row(&ut);
335 return;
336 }
337
338 /* Not found; fill the utmp structure with the information we have. */
339 memset(&ut, 0, sizeof(ut));
340 if ((pwd = getpwuid(getuid())) != NULL)
341 name = pwd->pw_name;
342 else
343 name = "?";
344 strncpy(ut.ut_name, name, UT_NAMESIZE);
345 strncpy(ut.ut_line, tty, UT_LINESIZE);
346 ut.ut_time = _time_to_time32(time(NULL));
347 row(&ut);
348 }
349
350 static int
351 ttywidth(void)
352 {
353 struct winsize ws;
354 long width;
355 char *cols, *ep;
356
357 if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
358 errno = 0;
359 width = strtol(cols, &ep, 10);
360 if (errno || width <= 0 || width > INT_MAX || ep == cols ||
361 *ep != '\0')
362 warnx("invalid COLUMNS environment variable ignored");
363 else
364 return (width);
365 }
366 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
367 return (ws.ws_col);
368
369 return (80);
370 }