Mechanically convert mge(4) to IfAPI
[freebsd.git] / usr.bin / who / who.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2002 Tim J. Robbins.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36
37 #include <err.h>
38 #include <errno.h>
39 #include <langinfo.h>
40 #include <limits.h>
41 #include <locale.h>
42 #include <paths.h>
43 #include <pwd.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <time.h>
48 #include <timeconv.h>
49 #include <unistd.h>
50 #include <utmpx.h>
51
52 static void     heading(void);
53 static void     process_utmp(void);
54 static void     quick(void);
55 static void     row(const struct utmpx *);
56 static int      ttywidth(void);
57 static void     usage(void);
58 static void     whoami(void);
59
60 static int      Hflag;                  /* Write column headings */
61 static int      aflag;                  /* Print all entries */
62 static int      bflag;                  /* Show date of the last reboot */
63 static int      mflag;                  /* Show info about current terminal */
64 static int      qflag;                  /* "Quick" mode */
65 static int      sflag;                  /* Show name, line, time */
66 static int      Tflag;                  /* Show terminal state */
67 static int      uflag;                  /* Show idle time */
68
69 int
70 main(int argc, char *argv[])
71 {
72         int ch;
73
74         setlocale(LC_TIME, "");
75
76         while ((ch = getopt(argc, argv, "HTabmqsu")) != -1) {
77                 switch (ch) {
78                 case 'H':               /* Write column headings */
79                         Hflag = 1;
80                         break;
81                 case 'T':               /* Show terminal state */
82                         Tflag = 1;
83                         break;
84                 case 'a':               /* Same as -bdlprtTu */
85                         aflag = bflag = Tflag = uflag = 1;
86                         break;
87                 case 'b':               /* Show date of the last reboot */
88                         bflag = 1;
89                         break;
90                 case 'm':               /* Show info about current terminal */
91                         mflag = 1;
92                         break;
93                 case 'q':               /* "Quick" mode */
94                         qflag = 1;
95                         break;
96                 case 's':               /* Show name, line, time */
97                         sflag = 1;
98                         break;
99                 case 'u':               /* Show idle time */
100                         uflag = 1;
101                         break;
102                 default:
103                         usage();
104                         /*NOTREACHED*/
105                 }
106         }
107         argc -= optind;
108         argv += optind;
109
110         if (argc >= 2 && strcmp(argv[0], "am") == 0 &&
111             (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) {
112                 /* "who am i" or "who am I", equivalent to -m */
113                 mflag = 1;
114                 argc -= 2;
115                 argv += 2;
116         }
117         if (argc > 1)
118                 usage();
119
120         if (*argv != NULL) {
121                 if (setutxdb(UTXDB_ACTIVE, *argv) != 0)
122                         err(1, "%s", *argv);
123         }
124
125         if (qflag)
126                 quick();
127         else {
128                 if (sflag)
129                         Tflag = uflag = 0;
130                 if (Hflag)
131                         heading();
132                 if (mflag)
133                         whoami();
134                 else
135                         process_utmp();
136         }
137
138         endutxent();
139
140         exit(0);
141 }
142
143 static void
144 usage(void)
145 {
146
147         fprintf(stderr, "usage: who [-abHmqsTu] [am I] [file]\n");
148         exit(1);
149 }
150
151 static void
152 heading(void)
153 {
154
155         printf("%-16s ", "NAME");
156         if (Tflag)
157                 printf("S ");
158         printf("%-12s %-12s ", "LINE", "TIME");
159         if (uflag)
160                 printf("IDLE  ");
161         printf("%-16s\n", "FROM");
162 }
163
164 static void
165 row(const struct utmpx *ut)
166 {
167         char buf[80], tty[PATH_MAX];
168         struct stat sb;
169         time_t idle, t;
170         static int d_first = -1;
171         struct tm *tm;
172         char state;
173
174         if (d_first < 0)
175                 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
176
177         state = '?';
178         idle = 0;
179         if (Tflag || uflag) {
180                 snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, ut->ut_line);
181                 if (stat(tty, &sb) == 0) {
182                         state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
183                             '+' : '-';
184                         idle = time(NULL) - sb.st_mtime;
185                 }
186         }
187
188         printf("%-16s ", ut->ut_user);
189         if (Tflag)
190                 printf("%c ", state);
191         if (ut->ut_type == BOOT_TIME)
192                 printf("%-12s ", "system boot");
193         else
194                 printf("%-12s ", ut->ut_line);
195         t = ut->ut_tv.tv_sec;
196         tm = localtime(&t);
197         strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
198         printf("%-*s ", 12, buf);
199         if (uflag) {
200                 if (idle < 60)
201                         printf("  .   ");
202                 else if (idle < 24 * 60 * 60)
203                         printf("%02d:%02d ", (int)(idle / 60 / 60),
204                             (int)(idle / 60 % 60));
205                 else
206                         printf(" old  ");
207         }
208         if (*ut->ut_host != '\0')
209                 printf("(%s)", ut->ut_host);
210         putchar('\n');
211 }
212
213 static int
214 ttystat(char *line)
215 {
216         struct stat sb;
217         char ttybuf[MAXPATHLEN];
218
219         if (line == NULL)
220                 return (0);
221         (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
222         if (stat(ttybuf, &sb) == 0) {
223                 return (0);
224         } else
225                 return (-1);
226 }
227
228 static void
229 process_utmp(void)
230 {
231         struct utmpx *utx;
232
233         while ((utx = getutxent()) != NULL) {
234                 if ((aflag || !bflag) && utx->ut_type == USER_PROCESS) {
235                         if (ttystat(utx->ut_line) == 0)
236                                 row(utx);
237                 } else if (bflag && utx->ut_type == BOOT_TIME)
238                                 row(utx);
239         }
240 }
241
242 static void
243 quick(void)
244 {
245         struct utmpx *utx;
246         int col, ncols, num;
247
248         ncols = ttywidth();
249         col = num = 0;
250         while ((utx = getutxent()) != NULL) {
251                 if (utx->ut_type != USER_PROCESS)
252                         continue;
253                 printf("%-16s", utx->ut_user);
254                 if (++col < ncols / (16 + 1))
255                         putchar(' ');
256                 else {
257                         col = 0;
258                         putchar('\n');
259                 }
260                 num++;
261         }
262         if (col != 0)
263                 putchar('\n');
264
265         printf("# users = %d\n", num);
266 }
267
268 static void
269 whoami(void)
270 {
271         struct utmpx ut, *utx;
272         struct passwd *pwd;
273         const char *name, *tty;
274
275         if ((tty = ttyname(STDIN_FILENO)) == NULL)
276                 tty = "tty??";
277         else if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
278                 tty += sizeof _PATH_DEV - 1;
279         strlcpy(ut.ut_line, tty, sizeof ut.ut_line);
280
281         /* Search utmp for our tty, dump first matching record. */
282         if ((utx = getutxline(&ut)) != NULL && utx->ut_type == USER_PROCESS) {
283                 row(utx);
284                 return;
285         }
286
287         /* Not found; fill the utmp structure with the information we have. */
288         memset(&ut, 0, sizeof(ut));
289         if ((pwd = getpwuid(getuid())) != NULL)
290                 name = pwd->pw_name;
291         else
292                 name = "?";
293         strlcpy(ut.ut_user, name, sizeof ut.ut_user);
294         gettimeofday(&ut.ut_tv, NULL);
295         row(&ut);
296 }
297
298 static int
299 ttywidth(void)
300 {
301         struct winsize ws;
302         long width;
303         char *cols, *ep;
304
305         if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
306                 errno = 0;
307                 width = strtol(cols, &ep, 10);
308                 if (errno || width <= 0 || width > INT_MAX || ep == cols ||
309                     *ep != '\0')
310                         warnx("invalid COLUMNS environment variable ignored");
311                 else
312                         return (width);
313         }
314         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
315                 return (ws.ws_col);
316
317         return (80);
318 }