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