]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.bin/who/who.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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 <utmpx.h>
49
50 static void     heading(void);
51 static void     process_utmp(void);
52 static void     quick(void);
53 static void     row(struct utmpx *);
54 static int      ttywidth(void);
55 static void     usage(void);
56 static void     whoami(void);
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
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                 if (setutxdb(UTXDB_ACTIVE, *argv) != 0)
112                         err(1, "%s", *argv);
113         }
114
115         if (qflag)
116                 quick();
117         else {
118                 if (sflag)
119                         Tflag = uflag = 0;
120                 if (Hflag)
121                         heading();
122                 if (mflag)
123                         whoami();
124                 else
125                         process_utmp();
126         }
127
128         endutxent();
129
130         exit(0);
131 }
132
133 static void
134 usage(void)
135 {
136
137         fprintf(stderr, "usage: who [-HmqsTu] [am I] [file]\n");
138         exit(1);
139 }
140
141 static void
142 heading(void)
143 {
144
145         printf("%-16s ", "NAME");
146         if (Tflag)
147                 printf("S ");
148         printf("%-8s %-12s ", "LINE", "TIME");
149         if (uflag)
150                 printf("IDLE  ");
151         printf("%-16s\n", "FROM");
152 }
153
154 static void
155 row(struct utmpx *ut)
156 {
157         char buf[80], tty[PATH_MAX];
158         struct stat sb;
159         time_t idle, t;
160         static int d_first = -1;
161         struct tm *tm;
162         char state;
163
164         if (d_first < 0)
165                 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
166
167         state = '?';
168         idle = 0;
169         if (Tflag || uflag) {
170                 snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, ut->ut_line);
171                 if (stat(tty, &sb) == 0) {
172                         state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
173                             '+' : '-';
174                         idle = time(NULL) - sb.st_mtime;
175                 }
176         }
177
178         printf("%-16s ", ut->ut_user);
179         if (Tflag)
180                 printf("%c ", state);
181         printf("%-8s ", ut->ut_line);
182         t = ut->ut_tv.tv_sec;
183         tm = localtime(&t);
184         strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
185         printf("%-*s ", 12, buf);
186         if (uflag) {
187                 if (idle < 60)
188                         printf("  .   ");
189                 else if (idle < 24 * 60 * 60)
190                         printf("%02d:%02d ", (int)(idle / 60 / 60),
191                             (int)(idle / 60 % 60));
192                 else
193                         printf(" old  ");
194         }
195         if (*ut->ut_host != '\0')
196                 printf("(%s)", ut->ut_host);
197         putchar('\n');
198 }
199
200 static int
201 ttystat(char *line)
202 {
203         struct stat sb;
204         char ttybuf[MAXPATHLEN];
205
206         (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
207         if (stat(ttybuf, &sb) == 0) {
208                 return (0);
209         } else
210                 return (-1);
211 }
212
213 static void
214 process_utmp(void)
215 {
216         struct utmpx *utx;
217
218         while ((utx = getutxent()) != NULL) {
219                 if (utx->ut_type != USER_PROCESS)
220                         continue;
221                 if (ttystat(utx->ut_line) != 0)
222                         continue;
223                 row(utx);
224         }
225 }
226
227 static void
228 quick(void)
229 {
230         struct utmpx *utx;
231         int col, ncols, num;
232
233         ncols = ttywidth();
234         col = num = 0;
235         while ((utx = getutxent()) != NULL) {
236                 if (utx->ut_type != USER_PROCESS)
237                         continue;
238                 printf("%-16s", utx->ut_user);
239                 if (++col < ncols / (16 + 1))
240                         putchar(' ');
241                 else {
242                         col = 0;
243                         putchar('\n');
244                 }
245                 num++;
246         }
247         if (col != 0)
248                 putchar('\n');
249
250         printf("# users = %d\n", num);
251 }
252
253 static void
254 whoami(void)
255 {
256         struct utmpx ut, *utx;
257         struct passwd *pwd;
258         const char *name, *tty;
259
260         if ((tty = ttyname(STDIN_FILENO)) == NULL)
261                 tty = "tty??";
262         else if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
263                 tty += sizeof _PATH_DEV - 1;
264         strlcpy(ut.ut_line, tty, sizeof ut.ut_line);
265
266         /* Search utmp for our tty, dump first matching record. */
267         if ((utx = getutxline(&ut)) != NULL && utx->ut_type == USER_PROCESS) {
268                 row(utx);
269                 return;
270         }
271
272         /* Not found; fill the utmp structure with the information we have. */
273         memset(&ut, 0, sizeof(ut));
274         if ((pwd = getpwuid(getuid())) != NULL)
275                 name = pwd->pw_name;
276         else
277                 name = "?";
278         strlcpy(ut.ut_user, name, sizeof ut.ut_user);
279         gettimeofday(&ut.ut_tv, NULL);
280         row(&ut);
281 }
282
283 static int
284 ttywidth(void)
285 {
286         struct winsize ws;
287         long width;
288         char *cols, *ep;
289
290         if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
291                 errno = 0;
292                 width = strtol(cols, &ep, 10);
293                 if (errno || width <= 0 || width > INT_MAX || ep == cols ||
294                     *ep != '\0')
295                         warnx("invalid COLUMNS environment variable ignored");
296                 else
297                         return (width);
298         }
299         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
300                 return (ws.ws_col);
301
302         return (80);
303 }