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