Commit | Line | Data |
---|---|---|
5f487bdd | 1 | /*- |
a79fea2f | 2 | * Copyright (c) 1980, 1991, 1993, 1994 |
ce165a45 | 3 | * The Regents of the University of California. All rights reserved. |
5f487bdd | 4 | * |
48dbeea8 | 5 | * %sccs.include.redist.c% |
f42904bc DF |
6 | */ |
7 | ||
8 | #ifndef lint | |
ce165a45 | 9 | static char copyright[] = |
a79fea2f | 10 | "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ |
ce165a45 | 11 | The Regents of the University of California. All rights reserved.\n"; |
5f487bdd | 12 | #endif /* not lint */ |
f42904bc | 13 | |
68d13ee3 | 14 | #ifndef lint |
a79fea2f | 15 | static char sccsid[] = "@(#)w.c 8.2 (Berkeley) %G%"; |
5f487bdd | 16 | #endif /* not lint */ |
f42904bc | 17 | |
76238d88 BJ |
18 | /* |
19 | * w - print system status (who and what) | |
20 | * | |
21 | * This program is similar to the systat command on Tenex/Tops 10/20 | |
134b296f | 22 | * |
76238d88 BJ |
23 | */ |
24 | #include <sys/param.h> | |
76aee4b1 | 25 | #include <sys/time.h> |
76238d88 | 26 | #include <sys/stat.h> |
dc602bdf | 27 | #include <sys/sysctl.h> |
76238d88 | 28 | #include <sys/proc.h> |
76aee4b1 | 29 | #include <sys/user.h> |
3b8dff21 | 30 | #include <sys/ioctl.h> |
f9a38dfe | 31 | #include <sys/socket.h> |
af08d75a | 32 | #include <sys/tty.h> |
f9a38dfe | 33 | |
3d68b3c1 | 34 | #include <machine/cpu.h> |
f9a38dfe KB |
35 | #include <netinet/in.h> |
36 | #include <arpa/inet.h> | |
37 | ||
134b296f | 38 | #include <ctype.h> |
f9a38dfe | 39 | #include <err.h> |
0e91cc75 | 40 | #include <errno.h> |
f9a38dfe | 41 | #include <fcntl.h> |
1196444f | 42 | #include <kvm.h> |
f9a38dfe | 43 | #include <netdb.h> |
1196444f | 44 | #include <nlist.h> |
f9a38dfe | 45 | #include <paths.h> |
134b296f | 46 | #include <stdio.h> |
1196444f KM |
47 | #include <stdlib.h> |
48 | #include <string.h> | |
f9a38dfe KB |
49 | #include <tzfile.h> |
50 | #include <unistd.h> | |
1196444f | 51 | #include <utmp.h> |
76238d88 | 52 | |
f9a38dfe | 53 | #include "extern.h" |
76aee4b1 | 54 | |
a79fea2f JSP |
55 | struct timeval boottime; |
56 | struct utmp utmp; | |
57 | struct winsize ws; | |
58 | kvm_t *kd; | |
59 | time_t now; /* the current time of day */ | |
60 | time_t uptime; /* time of last reboot & elapsed time since */ | |
61 | int ttywidth; /* width of tty */ | |
62 | int argwidth; /* width of tty */ | |
63 | int header = 1; /* true if -h flag: don't print heading */ | |
64 | int nflag; /* true if -n flag: don't convert addrs */ | |
65 | int sortidle; /* sort bu idle time */ | |
66 | char *sel_user; /* login of particular user selected */ | |
67 | char domain[MAXHOSTNAMELEN]; | |
134b296f MT |
68 | |
69 | /* | |
1196444f | 70 | * One of these per active utmp entry. |
134b296f MT |
71 | */ |
72 | struct entry { | |
73 | struct entry *next; | |
74 | struct utmp utmp; | |
75 | dev_t tdev; /* dev_t of terminal */ | |
f9a38dfe | 76 | time_t idle; /* idle time of terminal in seconds */ |
1196444f | 77 | struct kinfo_proc *kp; /* `most interesting' proc */ |
134b296f MT |
78 | char *args; /* arg list of interesting process */ |
79 | } *ep, *ehead = NULL, **nextp = &ehead; | |
80 | ||
3d68b3c1 | 81 | static void pr_header __P((time_t *, int)); |
f9a38dfe KB |
82 | static struct stat |
83 | *ttystat __P((char *)); | |
84 | static void usage __P((int)); | |
76238d88 | 85 | |
a79fea2f | 86 | char *fmt_argv __P((char **, char *, int)); /* ../../bin/ps/fmt.c */ |
fc8009ef | 87 | |
f9a38dfe | 88 | int |
76238d88 | 89 | main(argc, argv) |
48dbeea8 | 90 | int argc; |
76238d88 BJ |
91 | char **argv; |
92 | { | |
a79fea2f | 93 | extern char *__progname; |
f9a38dfe KB |
94 | struct kinfo_proc *kp; |
95 | struct hostent *hp; | |
96 | struct stat *stp; | |
97 | FILE *ut; | |
98 | u_long l; | |
99 | int ch, i, nentries, nusers, wcmd; | |
100 | char *memf, *nlistf, *p, *x; | |
101 | char buf[MAXHOSTNAMELEN], errbuf[256]; | |
76238d88 | 102 | |
f9a38dfe | 103 | /* Are we w(1) or uptime(1)? */ |
a79fea2f JSP |
104 | p = __progname; |
105 | if (*p == '-') | |
f9a38dfe KB |
106 | p++; |
107 | if (*p == 'u') { | |
134b296f | 108 | wcmd = 0; |
f9a38dfe KB |
109 | p = ""; |
110 | } else { | |
111 | wcmd = 1; | |
112 | p = "hiflM:N:nsuw"; | |
113 | } | |
134b296f | 114 | |
f9a38dfe KB |
115 | memf = nlistf = NULL; |
116 | while ((ch = getopt(argc, argv, p)) != EOF) | |
117 | switch (ch) { | |
134b296f MT |
118 | case 'h': |
119 | header = 0; | |
120 | break; | |
ee8a3c9d | 121 | case 'i': |
f9a38dfe KB |
122 | sortidle = 1; |
123 | break; | |
124 | case 'M': | |
3d68b3c1 | 125 | header = 0; |
f9a38dfe KB |
126 | memf = optarg; |
127 | break; | |
128 | case 'N': | |
129 | nlistf = optarg; | |
134b296f | 130 | break; |
1196444f | 131 | case 'n': |
f9a38dfe | 132 | nflag = 1; |
1196444f | 133 | break; |
134b296f | 134 | case 'f': case 'l': case 's': case 'u': case 'w': |
f9a38dfe KB |
135 | warnx("[-flsuw] no longer supported"); |
136 | /* FALLTHROUGH */ | |
134b296f MT |
137 | case '?': |
138 | default: | |
f9a38dfe | 139 | usage(wcmd); |
76238d88 | 140 | } |
134b296f MT |
141 | argc -= optind; |
142 | argv += optind; | |
48dbeea8 | 143 | |
f9a38dfe | 144 | if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL) |
a79fea2f | 145 | errx(1, "%s", errbuf); |
f9a38dfe KB |
146 | |
147 | (void)time(&now); | |
148 | if ((ut = fopen(_PATH_UTMP, "r")) == NULL) | |
149 | err(1, "%s", _PATH_UTMP); | |
150 | ||
48dbeea8 KB |
151 | if (*argv) |
152 | sel_user = *argv; | |
76238d88 | 153 | |
f9a38dfe | 154 | for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) { |
134b296f MT |
155 | if (utmp.ut_name[0] == '\0') |
156 | continue; | |
f9a38dfe | 157 | ++nusers; |
1196444f | 158 | if (wcmd == 0 || (sel_user && |
134b296f MT |
159 | strncmp(utmp.ut_name, sel_user, UT_NAMESIZE) != 0)) |
160 | continue; | |
f9a38dfe KB |
161 | if ((ep = calloc(1, sizeof(struct entry))) == NULL) |
162 | err(1, NULL); | |
134b296f MT |
163 | *nextp = ep; |
164 | nextp = &(ep->next); | |
a79fea2f | 165 | memmove(&(ep->utmp), &utmp, sizeof(struct utmp)); |
134b296f MT |
166 | stp = ttystat(ep->utmp.ut_line); |
167 | ep->tdev = stp->st_rdev; | |
3d68b3c1 | 168 | #ifdef CPU_CONSDEV |
958d0e58 | 169 | /* |
f9a38dfe | 170 | * If this is the console device, attempt to ascertain |
958d0e58 KB |
171 | * the true console device dev_t. |
172 | */ | |
173 | if (ep->tdev == 0) { | |
83c99760 CT |
174 | int mib[2]; |
175 | size_t size; | |
3d68b3c1 KM |
176 | |
177 | mib[0] = CTL_MACHDEP; | |
178 | mib[1] = CPU_CONSDEV; | |
179 | size = sizeof(dev_t); | |
180 | (void) sysctl(mib, 2, &ep->tdev, &size, NULL, 0); | |
958d0e58 KB |
181 | } |
182 | #endif | |
f9a38dfe | 183 | if ((ep->idle = now - stp->st_atime) < 0) |
134b296f MT |
184 | ep->idle = 0; |
185 | } | |
f9a38dfe | 186 | (void)fclose(ut); |
134b296f MT |
187 | |
188 | if (header || wcmd == 0) { | |
3d68b3c1 | 189 | pr_header(&now, nusers); |
f9a38dfe KB |
190 | if (wcmd == 0) |
191 | exit (0); | |
192 | } | |
76238d88 | 193 | |
134b296f MT |
194 | #define HEADER "USER TTY FROM LOGIN@ IDLE WHAT\n" |
195 | #define WUSED (sizeof (HEADER) - sizeof ("WHAT\n")) | |
f9a38dfe | 196 | (void)printf(HEADER); |
76238d88 | 197 | |
dc602bdf | 198 | if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) |
f9a38dfe | 199 | err(1, "%s", kvm_geterr(kd)); |
1196444f | 200 | for (i = 0; i < nentries; i++, kp++) { |
a79fea2f JSP |
201 | struct proc *p = &kp->kp_proc; |
202 | struct eproc *e; | |
1196444f KM |
203 | |
204 | if (p->p_stat == SIDL || p->p_stat == SZOMB) | |
134b296f | 205 | continue; |
1196444f | 206 | e = &kp->kp_eproc; |
134b296f MT |
207 | for (ep = ehead; ep != NULL; ep = ep->next) { |
208 | if (ep->tdev == e->e_tdev && e->e_pgid == e->e_tpgid) { | |
209 | /* | |
210 | * Proc is in foreground of this terminal | |
211 | */ | |
1196444f KM |
212 | if (proc_compare(&ep->kp->kp_proc, p)) |
213 | ep->kp = kp; | |
134b296f | 214 | break; |
76238d88 BJ |
215 | } |
216 | } | |
76238d88 | 217 | } |
f9a38dfe KB |
218 | if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && |
219 | ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && | |
220 | ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) | |
134b296f MT |
221 | ttywidth = 79; |
222 | else | |
223 | ttywidth = ws.ws_col - 1; | |
224 | argwidth = ttywidth - WUSED; | |
225 | if (argwidth < 4) | |
226 | argwidth = 8; | |
227 | for (ep = ehead; ep != NULL; ep = ep->next) { | |
1196444f | 228 | if (ep->kp == NULL) { |
f9b0d2b1 CL |
229 | ep->args = "-"; |
230 | continue; | |
231 | } | |
1196444f KM |
232 | ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth), |
233 | ep->kp->kp_proc.p_comm, MAXCOMLEN); | |
f9a38dfe KB |
234 | if (ep->args == NULL) |
235 | err(1, NULL); | |
134b296f MT |
236 | } |
237 | /* sort by idle time */ | |
238 | if (sortidle && ehead != NULL) { | |
239 | struct entry *from = ehead, *save; | |
240 | ||
241 | ehead = NULL; | |
242 | while (from != NULL) { | |
1196444f | 243 | for (nextp = &ehead; |
134b296f MT |
244 | (*nextp) && from->idle >= (*nextp)->idle; |
245 | nextp = &(*nextp)->next) | |
1196444f | 246 | continue; |
134b296f MT |
247 | save = from; |
248 | from = from->next; | |
249 | save->next = *nextp; | |
250 | *nextp = save; | |
251 | } | |
252 | } | |
253 | ||
f9a38dfe KB |
254 | if (!nflag) |
255 | if (gethostname(domain, sizeof(domain) - 1) < 0 || | |
a79fea2f | 256 | (p = strchr(domain, '.')) == 0) |
f9a38dfe KB |
257 | domain[0] = '\0'; |
258 | else { | |
259 | domain[sizeof(domain) - 1] = '\0'; | |
a79fea2f | 260 | memmove(domain, p, strlen(p) + 1); |
f9a38dfe | 261 | } |
1196444f | 262 | |
f9a38dfe KB |
263 | for (ep = ehead; ep != NULL; ep = ep->next) { |
264 | p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-"; | |
a79fea2f | 265 | if (x = strchr(p, ':')) |
1196444f | 266 | *x++ = '\0'; |
f9a38dfe KB |
267 | if (!nflag && isdigit(*p) && |
268 | (long)(l = inet_addr(p)) != -1 && | |
269 | (hp = gethostbyaddr((char *)&l, sizeof(l), AF_INET))) { | |
270 | if (domain[0] != '\0') { | |
271 | p = hp->h_name; | |
272 | p += strlen(hp->h_name); | |
273 | p -= strlen(domain); | |
a79fea2f | 274 | if (p > hp->h_name && strcmp(p, domain) == 0) |
f9a38dfe KB |
275 | *p = '\0'; |
276 | } | |
277 | p = hp->h_name; | |
1196444f KM |
278 | } |
279 | if (x) { | |
f9a38dfe KB |
280 | (void)snprintf(buf, sizeof(buf), "%s:%s", p, x); |
281 | p = buf; | |
1196444f | 282 | } |
f9a38dfe KB |
283 | (void)printf("%-*.*s %-2.2s %-*.*s ", |
284 | UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name, | |
285 | strncmp(ep->utmp.ut_line, "tty", 3) ? | |
286 | ep->utmp.ut_line : ep->utmp.ut_line + 3, | |
287 | UT_HOSTSIZE, UT_HOSTSIZE, *p ? p : "-"); | |
288 | pr_attime(&ep->utmp.ut_time, &now); | |
289 | pr_idle(ep->idle); | |
290 | (void)printf("%.*s\n", argwidth, ep->args); | |
134b296f | 291 | } |
86858041 | 292 | exit(0); |
76238d88 BJ |
293 | } |
294 | ||
f9a38dfe | 295 | static void |
3d68b3c1 | 296 | pr_header(nowp, nusers) |
f9a38dfe KB |
297 | time_t *nowp; |
298 | int nusers; | |
134b296f | 299 | { |
f9a38dfe KB |
300 | double avenrun[3]; |
301 | time_t uptime; | |
302 | int days, hrs, i, mins; | |
83c99760 CT |
303 | int mib[2]; |
304 | size_t size; | |
a79fea2f | 305 | char buf[256]; |
f9a38dfe | 306 | |
fc8009ef KB |
307 | /* |
308 | * Print time of day. | |
309 | * | |
310 | * Note, SCCS forces the string manipulation below, as it | |
311 | * replaces w.c with file information. | |
312 | */ | |
a79fea2f | 313 | (void)strftime(buf,sizeof(buf),__CONCAT("%l:%","M%p"),localtime(nowp)); |
f9a38dfe KB |
314 | (void)printf("%s ", buf); |
315 | ||
316 | /* | |
317 | * Print how long system has been up. | |
3d68b3c1 | 318 | * (Found by looking getting "boottime" from the kernel) |
f9a38dfe | 319 | */ |
3d68b3c1 KM |
320 | mib[0] = CTL_KERN; |
321 | mib[1] = KERN_BOOTTIME; | |
322 | size = sizeof(boottime); | |
6871ff00 KM |
323 | if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && |
324 | boottime.tv_sec != 0) { | |
325 | uptime = now - boottime.tv_sec; | |
326 | uptime += 30; | |
327 | days = uptime / SECSPERDAY; | |
328 | uptime %= SECSPERDAY; | |
329 | hrs = uptime / SECSPERHOUR; | |
330 | uptime %= SECSPERHOUR; | |
331 | mins = uptime / SECSPERMIN; | |
332 | (void)printf(" up"); | |
333 | if (days > 0) | |
334 | (void)printf(" %d day%s,", days, days > 1 ? "s" : ""); | |
335 | if (hrs > 0 && mins > 0) | |
336 | (void)printf(" %2d:%02d,", hrs, mins); | |
337 | else { | |
338 | if (hrs > 0) | |
339 | (void)printf(" %d hr%s,", | |
340 | hrs, hrs > 1 ? "s" : ""); | |
341 | if (mins > 0) | |
342 | (void)printf(" %d min%s,", | |
343 | mins, mins > 1 ? "s" : ""); | |
344 | } | |
f9a38dfe | 345 | } |
134b296f | 346 | |
f9a38dfe KB |
347 | /* Print number of users logged in to system */ |
348 | (void)printf(" %d user%s", nusers, nusers > 1 ? "s" : ""); | |
76238d88 | 349 | |
f9a38dfe KB |
350 | /* |
351 | * Print 1, 5, and 15 minute load averages. | |
352 | */ | |
3d68b3c1 | 353 | if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) |
f9a38dfe KB |
354 | (void)printf(", no load average information available\n"); |
355 | else { | |
356 | (void)printf(", load averages:"); | |
357 | for (i = 0; i < (sizeof(avenrun) / sizeof(avenrun[0])); i++) { | |
358 | if (i > 0) | |
359 | (void)printf(","); | |
360 | (void)printf(" %.2f", avenrun[i]); | |
361 | } | |
362 | (void)printf("\n"); | |
363 | } | |
76238d88 BJ |
364 | } |
365 | ||
f9a38dfe KB |
366 | static struct stat * |
367 | ttystat(line) | |
368 | char *line; | |
76238d88 | 369 | { |
f9a38dfe KB |
370 | static struct stat sb; |
371 | char ttybuf[MAXPATHLEN]; | |
76238d88 | 372 | |
f9a38dfe KB |
373 | (void)snprintf(ttybuf, sizeof(ttybuf), "%s/%s", _PATH_DEV, line); |
374 | if (stat(ttybuf, &sb)) | |
375 | err(1, "%s", ttybuf); | |
376 | return (&sb); | |
76238d88 BJ |
377 | } |
378 | ||
f9a38dfe KB |
379 | static void |
380 | usage(wcmd) | |
381 | int wcmd; | |
76238d88 | 382 | { |
f9a38dfe KB |
383 | if (wcmd) |
384 | (void)fprintf(stderr, | |
385 | "usage: w: [-hin] [-M core] [-N system] [user]\n"); | |
386 | else | |
387 | (void)fprintf(stderr, "uptime\n"); | |
388 | exit (1); | |
76238d88 | 389 | } |