]> git.saurik.com Git - apple/shell_cmds.git/blame - who/who.c
shell_cmds-74.1.tar.gz
[apple/shell_cmds.git] / who / who.c
CommitLineData
1c4c78a5
A
1/*-
2 * Copyright (c) 2002 Tim J. Robbins.
3 * All rights reserved.
44bd5ea7
A
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.
44bd5ea7 13 *
1c4c78a5 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
44bd5ea7
A
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1c4c78a5 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
44bd5ea7
A
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>
1c4c78a5
A
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*/
44bd5ea7
A
31
32#include <sys/types.h>
1c4c78a5 33#include <sys/ioctl.h>
44bd5ea7 34#include <sys/stat.h>
1c4c78a5 35
44bd5ea7 36#include <err.h>
1c4c78a5
A
37#include <errno.h>
38#include <langinfo.h>
39#include <limits.h>
44bd5ea7 40#include <locale.h>
1c4c78a5 41#include <paths.h>
44bd5ea7
A
42#include <pwd.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <time.h>
1c4c78a5 47#include <timeconv.h>
44bd5ea7
A
48#include <unistd.h>
49#include <utmp.h>
50
1c4c78a5
A
51static void heading(void);
52static void process_utmp(FILE *);
53static void process_wtmp(FILE *);
54static void quick(FILE *);
55static void row(struct utmp *);
56static int ttywidth(void);
57static void usage(void);
58static void whoami(FILE *);
59
60static int bflag; /* date & time of last reboot */
61static int dflag; /* dead processes */
62static int Hflag; /* Write column headings */
63static int lflag; /* waiting to login */
64static int mflag; /* Show info about current terminal */
65static int pflag; /* Processes active & spawned by init */
66static int qflag; /* "Quick" mode */
67static int rflag; /* run-level of the init process */
68static int sflag; /* Show name, line, time */
69static int tflag; /* time of change to system clock */
70static int Tflag; /* Show terminal state */
71static int uflag; /* Show idle time */
44bd5ea7
A
72
73int
1c4c78a5 74main(int argc, char *argv[])
44bd5ea7 75{
1c4c78a5
A
76 int ch;
77 const char *file;
78 FILE *fp;
79 FILE *wtmp_fp;
80
81 setlocale(LC_TIME, "");
44bd5ea7 82
1c4c78a5
A
83 while ((ch = getopt(argc, argv, "abdHlmpqrstTu")) != -1) {
84 switch (ch) {
44bd5ea7 85
1c4c78a5
A
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;
44bd5ea7 110 break;
1c4c78a5
A
111 case 'r': /* run-level of the init process */
112 rflag = 1;
44bd5ea7 113 break;
1c4c78a5
A
114 case 's': /* Show name, line, time */
115 sflag = 1;
44bd5ea7 116 break;
1c4c78a5
A
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;
44bd5ea7
A
125 break;
126 default:
127 usage();
1c4c78a5 128 /*NOTREACHED*/
44bd5ea7
A
129 }
130 }
131 argc -= optind;
132 argv += optind;
133
1c4c78a5
A
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;
44bd5ea7 140 }
1c4c78a5
A
141 if (argc > 1)
142 usage();
44bd5ea7 143
1c4c78a5
A
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 }
44bd5ea7 165
1c4c78a5 166 fclose(fp);
44bd5ea7 167
1c4c78a5
A
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);
44bd5ea7 177 }
44bd5ea7 178 }
1c4c78a5 179
44bd5ea7
A
180 exit(0);
181}
182
1c4c78a5
A
183static void
184usage(void)
44bd5ea7 185{
1c4c78a5
A
186
187 fprintf(stderr, "usage: who [-abdHlmpqrstTu] [am I] [file]\n");
188 exit(1);
44bd5ea7
A
189}
190
1c4c78a5
A
191static void
192heading(void)
44bd5ea7 193{
1c4c78a5
A
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
206static void
207row(struct utmp *ut)
208{
209 char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE];
44bd5ea7 210 struct stat sb;
1c4c78a5
A
211 time_t idle, t;
212 static int d_first = -1;
213 struct tm *tm;
44bd5ea7 214 char state;
44bd5ea7 215
1c4c78a5
A
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 }
44bd5ea7
A
229 }
230
1c4c78a5
A
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));
44bd5ea7 245 else
1c4c78a5 246 printf(" old ");
44bd5ea7 247 }
1c4c78a5
A
248 if (*ut->ut_host != '\0')
249 printf("(%.*s)", UT_HOSTSIZE, ut->ut_host);
250 putchar('\n');
251
44bd5ea7
A
252}
253
1c4c78a5
A
254static void
255process_utmp(FILE *fp)
44bd5ea7 256{
1c4c78a5 257 struct utmp ut;
44bd5ea7 258
1c4c78a5
A
259 while (fread(&ut, sizeof(ut), 1, fp) == 1)
260 if (*ut.ut_name != '\0') {
261 row(&ut);
262 }
263}
44bd5ea7 264
1c4c78a5
A
265/* For some options, process the wtmp file to generate output */
266static void
267process_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
292static void
293quick(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');
44bd5ea7 314
1c4c78a5 315 printf("# users = %d\n", num);
44bd5ea7
A
316}
317
1c4c78a5
A
318static void
319whoami(FILE *fp)
44bd5ea7 320{
1c4c78a5
A
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 }
44bd5ea7 337
1c4c78a5
A
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);
44bd5ea7
A
348}
349
1c4c78a5
A
350static int
351ttywidth(void)
44bd5ea7 352{
1c4c78a5
A
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);
44bd5ea7 370}