]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.bin/who/who.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.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(const 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      aflag;                  /* Print all entries */
60 static int      bflag;                  /* Show date of the last reboot */
61 static int      mflag;                  /* Show info about current terminal */
62 static int      qflag;                  /* "Quick" mode */
63 static int      sflag;                  /* Show name, line, time */
64 static int      Tflag;                  /* Show terminal state */
65 static int      uflag;                  /* Show idle time */
66
67 int
68 main(int argc, char *argv[])
69 {
70         int ch;
71
72         setlocale(LC_TIME, "");
73
74         while ((ch = getopt(argc, argv, "HTabmqsu")) != -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 'a':               /* Same as -bdlprtTu */
83                         aflag = bflag = Tflag = uflag = 1;
84                         break;
85                 case 'b':               /* Show date of the last reboot */
86                         bflag = 1;
87                         break;
88                 case 'm':               /* Show info about current terminal */
89                         mflag = 1;
90                         break;
91                 case 'q':               /* "Quick" mode */
92                         qflag = 1;
93                         break;
94                 case 's':               /* Show name, line, time */
95                         sflag = 1;
96                         break;
97                 case 'u':               /* Show idle time */
98                         uflag = 1;
99                         break;
100                 default:
101                         usage();
102                         /*NOTREACHED*/
103                 }
104         }
105         argc -= optind;
106         argv += optind;
107
108         if (argc >= 2 && strcmp(argv[0], "am") == 0 &&
109             (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) {
110                 /* "who am i" or "who am I", equivalent to -m */
111                 mflag = 1;
112                 argc -= 2;
113                 argv += 2;
114         }
115         if (argc > 1)
116                 usage();
117
118         if (*argv != NULL) {
119                 if (setutxdb(UTXDB_ACTIVE, *argv) != 0)
120                         err(1, "%s", *argv);
121         }
122
123         if (qflag)
124                 quick();
125         else {
126                 if (sflag)
127                         Tflag = uflag = 0;
128                 if (Hflag)
129                         heading();
130                 if (mflag)
131                         whoami();
132                 else
133                         process_utmp();
134         }
135
136         endutxent();
137
138         exit(0);
139 }
140
141 static void
142 usage(void)
143 {
144
145         fprintf(stderr, "usage: who [-abHmqsTu] [am I] [file]\n");
146         exit(1);
147 }
148
149 static void
150 heading(void)
151 {
152
153         printf("%-16s ", "NAME");
154         if (Tflag)
155                 printf("S ");
156         printf("%-12s %-12s ", "LINE", "TIME");
157         if (uflag)
158                 printf("IDLE  ");
159         printf("%-16s\n", "FROM");
160 }
161
162 static void
163 row(const struct utmpx *ut)
164 {
165         char buf[80], tty[PATH_MAX];
166         struct stat sb;
167         time_t idle, t;
168         static int d_first = -1;
169         struct tm *tm;
170         char state;
171
172         if (d_first < 0)
173                 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
174
175         state = '?';
176         idle = 0;
177         if (Tflag || uflag) {
178                 snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, ut->ut_line);
179                 if (stat(tty, &sb) == 0) {
180                         state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
181                             '+' : '-';
182                         idle = time(NULL) - sb.st_mtime;
183                 }
184         }
185
186         printf("%-16s ", ut->ut_user);
187         if (Tflag)
188                 printf("%c ", state);
189         if (ut->ut_type == BOOT_TIME)
190                 printf("%-12s ", "system boot");
191         else
192                 printf("%-12s ", ut->ut_line);
193         t = ut->ut_tv.tv_sec;
194         tm = localtime(&t);
195         strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
196         printf("%-*s ", 12, buf);
197         if (uflag) {
198                 if (idle < 60)
199                         printf("  .   ");
200                 else if (idle < 24 * 60 * 60)
201                         printf("%02d:%02d ", (int)(idle / 60 / 60),
202                             (int)(idle / 60 % 60));
203                 else
204                         printf(" old  ");
205         }
206         if (*ut->ut_host != '\0')
207                 printf("(%s)", ut->ut_host);
208         putchar('\n');
209 }
210
211 static int
212 ttystat(char *line)
213 {
214         struct stat sb;
215         char ttybuf[MAXPATHLEN];
216
217         (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
218         if (stat(ttybuf, &sb) == 0) {
219                 return (0);
220         } else
221                 return (-1);
222 }
223
224 static void
225 process_utmp(void)
226 {
227         struct utmpx *utx;
228
229         while ((utx = getutxent()) != NULL) {
230                 if (((aflag || !bflag) && utx->ut_type == USER_PROCESS) ||
231                     (bflag && utx->ut_type == BOOT_TIME))
232                         if (ttystat(utx->ut_line) == 0)
233                                 row(utx);
234         }
235 }
236
237 static void
238 quick(void)
239 {
240         struct utmpx *utx;
241         int col, ncols, num;
242
243         ncols = ttywidth();
244         col = num = 0;
245         while ((utx = getutxent()) != NULL) {
246                 if (utx->ut_type != USER_PROCESS)
247                         continue;
248                 printf("%-16s", utx->ut_user);
249                 if (++col < ncols / (16 + 1))
250                         putchar(' ');
251                 else {
252                         col = 0;
253                         putchar('\n');
254                 }
255                 num++;
256         }
257         if (col != 0)
258                 putchar('\n');
259
260         printf("# users = %d\n", num);
261 }
262
263 static void
264 whoami(void)
265 {
266         struct utmpx ut, *utx;
267         struct passwd *pwd;
268         const char *name, *tty;
269
270         if ((tty = ttyname(STDIN_FILENO)) == NULL)
271                 tty = "tty??";
272         else if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
273                 tty += sizeof _PATH_DEV - 1;
274         strlcpy(ut.ut_line, tty, sizeof ut.ut_line);
275
276         /* Search utmp for our tty, dump first matching record. */
277         if ((utx = getutxline(&ut)) != NULL && utx->ut_type == USER_PROCESS) {
278                 row(utx);
279                 return;
280         }
281
282         /* Not found; fill the utmp structure with the information we have. */
283         memset(&ut, 0, sizeof(ut));
284         if ((pwd = getpwuid(getuid())) != NULL)
285                 name = pwd->pw_name;
286         else
287                 name = "?";
288         strlcpy(ut.ut_user, name, sizeof ut.ut_user);
289         gettimeofday(&ut.ut_tv, NULL);
290         row(&ut);
291 }
292
293 static int
294 ttywidth(void)
295 {
296         struct winsize ws;
297         long width;
298         char *cols, *ep;
299
300         if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
301                 errno = 0;
302                 width = strtol(cols, &ep, 10);
303                 if (errno || width <= 0 || width > INT_MAX || ep == cols ||
304                     *ep != '\0')
305                         warnx("invalid COLUMNS environment variable ignored");
306                 else
307                         return (width);
308         }
309         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
310                 return (ws.ws_col);
311
312         return (80);
313 }