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