]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.bin/who/who.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.bin / 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 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/ioctl.h>
33 #include <sys/stat.h>
34
35 #include <err.h>
36 #include <errno.h>
37 #include <langinfo.h>
38 #include <limits.h>
39 #include <locale.h>
40 #include <paths.h>
41 #include <pwd.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #include <timeconv.h>
47 #include <unistd.h>
48 #include <utmp.h>
49
50 static void     heading(void);
51 static void     process_utmp(FILE *);
52 static void     quick(FILE *);
53 static void     row(struct utmp *);
54 static int      ttywidth(void);
55 static void     usage(void);
56 static void     whoami(FILE *);
57
58 static int      Hflag;                  /* Write column headings */
59 static int      mflag;                  /* Show info about current terminal */
60 static int      qflag;                  /* "Quick" mode */
61 static int      sflag;                  /* Show name, line, time */
62 static int      Tflag;                  /* Show terminal state */
63 static int      uflag;                  /* Show idle time */
64
65 int
66 main(int argc, char *argv[])
67 {
68         int ch;
69         const char *file;
70         FILE *fp;
71
72         setlocale(LC_TIME, "");
73
74         while ((ch = getopt(argc, argv, "HTmqsu")) != -1) {
75                 switch (ch) {
76                 case 'H':               /* Write column headings */
77                         Hflag = 1;
78                         break;
79                 case 'T':               /* Show terminal state */
80                         Tflag = 1;
81                         break;
82                 case 'm':               /* Show info about current terminal */
83                         mflag = 1;
84                         break;
85                 case 'q':               /* "Quick" mode */
86                         qflag = 1;
87                         break;
88                 case 's':               /* Show name, line, time */
89                         sflag = 1;
90                         break;
91                 case 'u':               /* Show idle time */
92                         uflag = 1;
93                         break;
94                 default:
95                         usage();
96                         /*NOTREACHED*/
97                 }
98         }
99         argc -= optind;
100         argv += optind;
101
102         if (argc >= 2 && strcmp(argv[0], "am") == 0 &&
103             (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) {
104                 /* "who am i" or "who am I", equivalent to -m */
105                 mflag = 1;
106                 argc -= 2;
107                 argv += 2;
108         }
109         if (argc > 1)
110                 usage();
111
112         if (*argv != NULL)
113                 file = *argv;
114         else
115                 file = _PATH_UTMP;
116         if ((fp = fopen(file, "r")) == NULL)
117                 err(1, "%s", file);
118
119         if (qflag)
120                 quick(fp);
121         else {
122                 if (sflag)
123                         Tflag = uflag = 0;
124                 if (Hflag)
125                         heading();
126                 if (mflag)
127                         whoami(fp);
128                 else
129                         process_utmp(fp);
130         }
131
132         fclose(fp);
133
134         exit(0);
135 }
136
137 static void
138 usage(void)
139 {
140
141         fprintf(stderr, "usage: who [-HmqsTu] [am I] [file]\n");
142         exit(1);
143 }
144
145 static void
146 heading(void)
147 {
148
149         printf("%-*s ", UT_NAMESIZE, "NAME");
150         if (Tflag)
151                 printf("S ");
152         printf("%-*s ", UT_LINESIZE, "LINE");
153         printf("%-*s ", 12, "TIME");
154         if (uflag)
155                 printf("IDLE  ");
156         printf("%-*s", UT_HOSTSIZE, "FROM");
157         putchar('\n');
158 }
159
160 static void
161 row(struct utmp *ut)
162 {
163         char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE];
164         struct stat sb;
165         time_t idle, t;
166         static int d_first = -1;
167         struct tm *tm;
168         char state;
169
170         if (d_first < 0)
171                 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
172
173         state = '?';
174         idle = 0;
175         if (Tflag || uflag) {
176                 snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV,
177                         UT_LINESIZE, ut->ut_line);
178                 if (stat(tty, &sb) == 0) {
179                         state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
180                             '+' : '-';
181                         idle = time(NULL) - sb.st_mtime;
182                 }
183         }
184
185         printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name);
186         if (Tflag)
187                 printf("%c ", state);
188         printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line);
189         t = _time32_to_time(ut->ut_time);
190         tm = localtime(&t);
191         strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
192         printf("%-*s ", 12, buf);
193         if (uflag) {
194                 if (idle < 60)
195                         printf("  .   ");
196                 else if (idle < 24 * 60 * 60)
197                         printf("%02d:%02d ", (int)(idle / 60 / 60),
198                             (int)(idle / 60 % 60));
199                 else
200                         printf(" old  ");
201         }
202         if (*ut->ut_host != '\0')
203                 printf("(%.*s)", UT_HOSTSIZE, ut->ut_host);
204         putchar('\n');
205 }
206
207 static int
208 ttystat(char *line, int sz)
209 {
210         struct stat sb;
211         char ttybuf[MAXPATHLEN];
212
213         (void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line);
214         if (stat(ttybuf, &sb) == 0) {
215                 return (0);
216         } else
217                 return (-1);
218 }
219
220 static void
221 process_utmp(FILE *fp)
222 {
223         struct utmp ut;
224
225         while (fread(&ut, sizeof(ut), 1, fp) == 1) {
226                 if (*ut.ut_name == '\0')
227                         continue;
228                 if (ttystat(ut.ut_line, UT_LINESIZE) != 0)
229                         continue;
230                 row(&ut);
231         }
232 }
233
234 static void
235 quick(FILE *fp)
236 {
237         struct utmp ut;
238         int col, ncols, num;
239
240         ncols = ttywidth();
241         col = num = 0;
242         while (fread(&ut, sizeof(ut), 1, fp) == 1) { 
243                 if (*ut.ut_name == '\0')
244                         continue;
245                 printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name);
246                 if (++col < ncols / (UT_NAMESIZE + 1))
247                         putchar(' ');
248                 else {
249                         col = 0;
250                         putchar('\n');
251                 }
252                 num++;
253         }
254         if (col != 0)
255                 putchar('\n');
256
257         printf("# users = %d\n", num);
258 }
259
260 static void
261 whoami(FILE *fp)
262 {
263         struct utmp ut;
264         struct passwd *pwd;
265         const char *name, *p, *tty;
266
267         if ((tty = ttyname(STDIN_FILENO)) == NULL)
268                 tty = "tty??";
269         else if ((p = strrchr(tty, '/')) != NULL)
270                 tty = p + 1;
271
272         /* Search utmp for our tty, dump first matching record. */
273         while (fread(&ut, sizeof(ut), 1, fp) == 1)
274                 if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty,
275                     UT_LINESIZE) == 0) {
276                         row(&ut);
277                         return;
278                 }
279
280         /* Not found; fill the utmp structure with the information we have. */
281         memset(&ut, 0, sizeof(ut));
282         if ((pwd = getpwuid(getuid())) != NULL)
283                 name = pwd->pw_name;
284         else
285                 name = "?";
286         strncpy(ut.ut_name, name, UT_NAMESIZE);
287         strncpy(ut.ut_line, tty, UT_LINESIZE);
288         ut.ut_time = _time_to_time32(time(NULL));
289         row(&ut);
290 }
291
292 static int
293 ttywidth(void)
294 {
295         struct winsize ws;
296         long width;
297         char *cols, *ep;
298
299         if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
300                 errno = 0;
301                 width = strtol(cols, &ep, 10);
302                 if (errno || width <= 0 || width > INT_MAX || ep == cols ||
303                     *ep != '\0')
304                         warnx("invalid COLUMNS environment variable ignored");
305                 else
306                         return (width);
307         }
308         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
309                 return (ws.ws_col);
310
311         return (80);
312 }