Deleted Added
full compact
who.c (201033) who.c (202200)
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>
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: head/usr.bin/who/who.c 201033 2009-12-26 22:36:05Z ed $");
28__FBSDID("$FreeBSD: head/usr.bin/who/who.c 202200 2010-01-13 18:09:54Z ed $");
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>
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#define _ULOG_POSIX_NAMES
48#include <ulog.h>
49#include <unistd.h>
47#include <unistd.h>
48#include <utmpx.h>
50
51static void heading(void);
52static void process_utmp(void);
53static void quick(void);
54static void row(struct utmpx *);
55static int ttywidth(void);
56static void usage(void);
57static void whoami(void);
58
59static int Hflag; /* Write column headings */
60static int mflag; /* Show info about current terminal */
61static int qflag; /* "Quick" mode */
62static int sflag; /* Show name, line, time */
63static int Tflag; /* Show terminal state */
64static int uflag; /* Show idle time */
65
66int
67main(int argc, char *argv[])
68{
69 int ch;
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) {
49
50static void heading(void);
51static void process_utmp(void);
52static void quick(void);
53static void row(struct utmpx *);
54static int ttywidth(void);
55static void usage(void);
56static void whoami(void);
57
58static int Hflag; /* Write column headings */
59static int mflag; /* Show info about current terminal */
60static int qflag; /* "Quick" mode */
61static int sflag; /* Show name, line, time */
62static int Tflag; /* Show terminal state */
63static int uflag; /* Show idle time */
64
65int
66main(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) {
112 if (ulog_setutxfile(UTXI_TTY, *argv) != 0)
111 if (setutxdb(UTXDB_ACTIVE, *argv) != 0)
113 err(1, "%s", *argv);
114 }
115
116 if (qflag)
117 quick();
118 else {
119 if (sflag)
120 Tflag = uflag = 0;
121 if (Hflag)
122 heading();
123 if (mflag)
124 whoami();
125 else
126 process_utmp();
127 }
128
129 endutxent();
130
131 exit(0);
132}
133
134static void
135usage(void)
136{
137
138 fprintf(stderr, "usage: who [-HmqsTu] [am I] [file]\n");
139 exit(1);
140}
141
142static void
143heading(void)
144{
145
146 printf("%-16s ", "NAME");
147 if (Tflag)
148 printf("S ");
149 printf("%-8s %-12s ", "LINE", "TIME");
150 if (uflag)
151 printf("IDLE ");
152 printf("%-16s\n", "FROM");
153}
154
155static void
156row(struct utmpx *ut)
157{
158 char buf[80], tty[PATH_MAX];
159 struct stat sb;
160 time_t idle, t;
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 state = '?';
169 idle = 0;
170 if (Tflag || uflag) {
171 snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, ut->ut_line);
172 if (stat(tty, &sb) == 0) {
173 state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
174 '+' : '-';
175 idle = time(NULL) - sb.st_mtime;
176 }
177 }
178
179 printf("%-16s ", ut->ut_user);
180 if (Tflag)
181 printf("%c ", state);
182 printf("%-8s ", ut->ut_line);
183 t = ut->ut_tv.tv_sec;
184 tm = localtime(&t);
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->ut_host);
198 putchar('\n');
199}
200
201static int
202ttystat(char *line)
203{
204 struct stat sb;
205 char ttybuf[MAXPATHLEN];
206
207 (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
208 if (stat(ttybuf, &sb) == 0) {
209 return (0);
210 } else
211 return (-1);
212}
213
214static void
215process_utmp(void)
216{
217 struct utmpx *utx;
218
219 while ((utx = getutxent()) != NULL) {
220 if (utx->ut_type != USER_PROCESS)
221 continue;
222 if (ttystat(utx->ut_line) != 0)
223 continue;
224 row(utx);
225 }
226}
227
228static void
229quick(void)
230{
231 struct utmpx *utx;
232 int col, ncols, num;
233
234 ncols = ttywidth();
235 col = num = 0;
236 while ((utx = getutxent()) != NULL) {
237 if (utx->ut_type != USER_PROCESS)
238 continue;
239 printf("%-16s", utx->ut_user);
240 if (++col < ncols / (16 + 1))
241 putchar(' ');
242 else {
243 col = 0;
244 putchar('\n');
245 }
246 num++;
247 }
248 if (col != 0)
249 putchar('\n');
250
251 printf("# users = %d\n", num);
252}
253
254static void
255whoami(void)
256{
257 struct utmpx ut, *utx;
258 struct passwd *pwd;
259 const char *name, *tty;
260
261 if ((tty = ttyname(STDIN_FILENO)) == NULL)
262 tty = "tty??";
263 else if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
264 tty += sizeof _PATH_DEV - 1;
265 strlcpy(ut.ut_line, tty, sizeof ut.ut_line);
266
267 /* Search utmp for our tty, dump first matching record. */
268 if ((utx = getutxline(&ut)) != NULL && utx->ut_type == USER_PROCESS) {
269 row(utx);
270 return;
271 }
272
273 /* Not found; fill the utmp structure with the information we have. */
274 memset(&ut, 0, sizeof(ut));
275 if ((pwd = getpwuid(getuid())) != NULL)
276 name = pwd->pw_name;
277 else
278 name = "?";
279 strlcpy(ut.ut_user, name, sizeof ut.ut_user);
280 gettimeofday(&ut.ut_tv, NULL);
281 row(&ut);
282}
283
284static int
285ttywidth(void)
286{
287 struct winsize ws;
288 long width;
289 char *cols, *ep;
290
291 if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
292 errno = 0;
293 width = strtol(cols, &ep, 10);
294 if (errno || width <= 0 || width > INT_MAX || ep == cols ||
295 *ep != '\0')
296 warnx("invalid COLUMNS environment variable ignored");
297 else
298 return (width);
299 }
300 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
301 return (ws.ws_col);
302
303 return (80);
304}
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
133static void
134usage(void)
135{
136
137 fprintf(stderr, "usage: who [-HmqsTu] [am I] [file]\n");
138 exit(1);
139}
140
141static void
142heading(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
154static void
155row(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
200static int
201ttystat(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
213static void
214process_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
227static void
228quick(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
253static void
254whoami(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
283static int
284ttywidth(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}