1 /* $OpenBSD: who.c,v 1.33 2021/11/11 08:48:48 mestre Exp $ */
2 /* $NetBSD: who.c,v 1.4 1994/12/07 04:28:49 jtc Exp $ */
3
4 /*
5 * Copyright (c) 1989, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Michael Fischbein.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <paths.h>
39 #include <pwd.h>
40 #include <utmp.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <time.h>
46 #include <err.h>
47
48 void output(struct utmp *);
49 void output_labels(void);
50 void who_am_i(FILE *);
51 void usage(void);
52 FILE *file(char *);
53
54 int only_current_term; /* show info about the current terminal only */
55 int show_term; /* show term state */
56 int show_idle; /* show idle time */
57 int show_labels; /* show column labels */
58 int show_quick; /* quick, names only */
59
60 #define NAME_WIDTH 8
61 #define HOST_WIDTH 45
62
63 int hostwidth = HOST_WIDTH;
64 char *mytty;
65
66 int
main(int argc,char * argv[])67 main(int argc, char *argv[])
68 {
69 struct utmp usr;
70 FILE *ufp;
71 char *t;
72 int c;
73
74 if (pledge("stdio unveil rpath getpw", NULL) == -1)
75 err(1, "pledge");
76
77 if ((mytty = ttyname(0))) {
78 /* strip any directory component */
79 if ((t = strrchr(mytty, '/')))
80 mytty = t + 1;
81 }
82
83 only_current_term = show_term = show_idle = show_labels = 0;
84 show_quick = 0;
85 while ((c = getopt(argc, argv, "HmqTu")) != -1) {
86 switch (c) {
87 case 'H':
88 show_labels = 1;
89 break;
90 case 'm':
91 only_current_term = 1;
92 break;
93 case 'q':
94 show_quick = 1;
95 break;
96 case 'T':
97 show_term = 1;
98 break;
99 case 'u':
100 show_idle = 1;
101 break;
102 default:
103 usage();
104 /* NOTREACHED */
105 }
106 }
107 argc -= optind;
108 argv += optind;
109
110 if (show_quick) {
111 only_current_term = show_term = show_idle = show_labels = 0;
112 }
113
114 if (show_term)
115 hostwidth -= 2;
116 if (show_idle)
117 hostwidth -= 6;
118
119 if (show_labels)
120 output_labels();
121
122 if (unveil(_PATH_UTMP, "r") == -1)
123 err(1, "unveil %s", _PATH_UTMP);
124 if (show_term || show_idle) {
125 if (unveil(_PATH_DEV, "r") == -1)
126 err(1, "unveil %s", _PATH_DEV);
127 }
128 if (argc == 1) {
129 if (unveil(*argv, "r") == -1)
130 err(1, "unveil %s", *argv);
131 }
132 if (pledge("stdio rpath getpw", NULL) == -1)
133 err(1, "pledge");
134
135 switch (argc) {
136 case 0: /* who */
137 ufp = file(_PATH_UTMP);
138
139 if (only_current_term) {
140 who_am_i(ufp);
141 } else if (show_quick) {
142 int count = 0;
143
144 while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1) {
145 if (*usr.ut_name && *usr.ut_line) {
146 (void)printf("%-*.*s ", NAME_WIDTH,
147 UT_NAMESIZE, usr.ut_name);
148 if ((++count % 8) == 0)
149 (void) printf("\n");
150 }
151 }
152 if (count % 8)
153 (void) printf("\n");
154 (void) printf ("# users=%d\n", count);
155 } else {
156 /* only entries with both name and line fields */
157 while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
158 if (*usr.ut_name && *usr.ut_line)
159 output(&usr);
160 }
161 break;
162 case 1: /* who utmp_file */
163 ufp = file(*argv);
164
165 if (only_current_term) {
166 who_am_i(ufp);
167 } else if (show_quick) {
168 int count = 0;
169
170 while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1) {
171 if (*usr.ut_name && *usr.ut_line) {
172 (void)printf("%-*.*s ", NAME_WIDTH,
173 UT_NAMESIZE, usr.ut_name);
174 if ((++count % 8) == 0)
175 (void) printf("\n");
176 }
177 }
178 if (count % 8)
179 (void) printf("\n");
180 (void) printf ("# users=%d\n", count);
181 } else {
182 /* all entries */
183 while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
184 output(&usr);
185 }
186 break;
187 case 2: /* who am i */
188 ufp = file(_PATH_UTMP);
189 who_am_i(ufp);
190 break;
191 default:
192 usage();
193 /* NOTREACHED */
194 }
195 exit(0);
196 }
197
198 void
who_am_i(FILE * ufp)199 who_am_i(FILE *ufp)
200 {
201 struct utmp usr;
202 struct passwd *pw;
203
204 /* search through the utmp and find an entry for this tty */
205 if (mytty) {
206 while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
207 if (*usr.ut_name && !strcmp(usr.ut_line, mytty)) {
208 output(&usr);
209 return;
210 }
211 /* well, at least we know what the tty is */
212 (void)strncpy(usr.ut_line, mytty, UT_LINESIZE);
213 } else
214 (void)strncpy(usr.ut_line, "tty??", UT_LINESIZE);
215
216 pw = getpwuid(getuid());
217 (void)strncpy(usr.ut_name, pw ? pw->pw_name : "?", UT_NAMESIZE);
218 (void)time(&usr.ut_time);
219 *usr.ut_host = '\0';
220 output(&usr);
221 }
222
223 void
output(struct utmp * up)224 output(struct utmp *up)
225 {
226 struct stat sb;
227 char line[sizeof(_PATH_DEV) + sizeof (up->ut_line)];
228 char state = '?';
229 static time_t now = 0;
230 time_t idle = 0;
231
232 if (show_term || show_idle) {
233 if (now == 0)
234 time(&now);
235
236 memset(line, 0, sizeof line);
237 strlcpy(line, _PATH_DEV, sizeof line);
238 strlcat(line, up->ut_line, sizeof line);
239
240 if (stat(line, &sb) == 0) {
241 state = (sb.st_mode & 020) ? '+' : '-';
242 idle = now - sb.st_atime;
243 } else {
244 state = '?';
245 idle = 0;
246 }
247 }
248
249 (void)printf("%-*.*s ", NAME_WIDTH, UT_NAMESIZE, up->ut_name);
250
251 if (show_term) {
252 (void)printf("%c ", state);
253 }
254
255 (void)printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, up->ut_line);
256 (void)printf("%.12s ", ctime(&up->ut_time) + 4);
257
258 if (show_idle) {
259 if (idle < 60)
260 (void)printf(" . ");
261 else if (idle < (24 * 60 * 60))
262 (void)printf("%02d:%02d ",
263 ((int)idle / (60 * 60)),
264 ((int)idle % (60 * 60)) / 60);
265 else
266 (void)printf(" old ");
267 }
268
269 if (*up->ut_host)
270 printf(" (%.*s)", hostwidth, up->ut_host);
271 (void)putchar('\n');
272 }
273
274 void
output_labels(void)275 output_labels(void)
276 {
277 (void)printf("%-*.*s ", NAME_WIDTH, UT_NAMESIZE, "USER");
278
279 if (show_term)
280 (void)printf("S ");
281
282 (void)printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, "LINE");
283 (void)printf("WHEN ");
284
285 if (show_idle)
286 (void)printf("IDLE ");
287
288 (void)printf(" %.*s", hostwidth, "FROM");
289
290 (void)putchar('\n');
291 }
292
293 FILE *
file(char * name)294 file(char *name)
295 {
296 FILE *ufp;
297
298 if (!(ufp = fopen(name, "r"))) {
299 err(1, "%s", name);
300 /* NOTREACHED */
301 }
302 if (!show_term && !show_idle) {
303 if (pledge("stdio getpw", NULL) == -1)
304 err(1, "pledge");
305 }
306 return(ufp);
307 }
308
309 void
usage(void)310 usage(void)
311 {
312 (void)fprintf(stderr, "usage: who [-HmqTu] [file]\n who am i\n");
313 exit(1);
314 }
315