2 * Copyright (c) 1980, 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
39 static const char copyright
[] =
40 "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
41 The Regents of the University of California. All rights reserved.\n";
45 static const char sccsid
[] = "@(#)w.c 8.4 (Berkeley) 4/16/94";
49 * w - print system status (who and what)
51 * This program is similar to the systat command on Tenex/Tops 10/20
54 #include <sys/param.h>
57 #include <sys/sysctl.h>
60 #include <sys/ioctl.h>
61 #include <sys/socket.h>
64 #include <machine/cpu.h>
65 #include <netinet/in.h>
66 #include <arpa/inet.h>
67 #include <arpa/nameser.h>
92 struct timeval boottime
;
96 time_t now
; /* the current time of day */
97 int ttywidth
; /* width of tty */
98 int argwidth
; /* width of tty */
99 int header
= 1; /* true if -h flag: don't print heading */
100 int nflag
; /* true if -n flag: don't convert addrs */
101 int dflag
; /* true if -d flag: output debug info */
102 int sortidle
; /* sort by idle time */
103 int use_ampm
; /* use AM/PM time */
104 int use_comma
; /* use comma as floats separator */
105 char **sel_users
; /* login array of particular users selected */
108 * One of these per active utmp entry.
113 dev_t tdev
; /* dev_t of terminal */
114 time_t idle
; /* idle time of terminal in seconds */
115 struct kinfo_proc
*kp
; /* `most interesting' proc */
116 char *args
; /* arg list of interesting process */
117 struct kinfo_proc
*dkp
; /* debug option proc list */
118 } *ep
, *ehead
= NULL
, **nextp
= &ehead
;
120 #define debugproc(p) *((struct kinfo_proc **)&(p)->ki_udata)
122 /* W_DISPHOSTSIZE should not be greater than UT_HOSTSIZE */
123 #define W_DISPHOSTSIZE 16
125 static void pr_header(time_t *, int);
126 static struct stat
*ttystat(char *, int);
127 static void usage(int);
128 static int this_is_uptime(const char *s
);
130 char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */
133 main(int argc
, char *argv
[])
135 struct kinfo_proc
*kp
;
136 struct kinfo_proc
*dkp
;
140 int ch
, i
, nentries
, nusers
, wcmd
, longidle
, dropgid
;
141 const char *memf
, *nlistf
, *p
;
143 char buf
[MAXHOSTNAMELEN
], errbuf
[_POSIX2_LINE_MAX
];
144 char fn
[MAXHOSTNAMELEN
];
147 (void)setlocale(LC_ALL
, "");
148 use_ampm
= (*nl_langinfo(T_FMT_AMPM
) != '\0');
149 use_comma
= (*nl_langinfo(RADIXCHAR
) != ',');
151 /* Are we w(1) or uptime(1)? */
152 if (this_is_uptime(argv
[0]) == 0) {
161 memf
= nlistf
= _PATH_DEVNULL
;
162 while ((ch
= getopt(argc
, argv
, p
)) != -1)
185 case 'f': case 'l': case 's': case 'u': case 'w':
186 warnx("[-flsuw] no longer supported");
195 if (!(_res
.options
& RES_INIT
))
197 _res
.retrans
= 2; /* resolver timeout to 2 seconds per try */
198 _res
.retry
= 1; /* only try once.. */
201 * Discard setgid privileges if not the running kernel so that bad
202 * guys can't print interesting stuff from kernel memory.
207 if ((kd
= kvm_openfiles(nlistf
, memf
, NULL
, O_RDONLY
, errbuf
)) == NULL
)
208 errx(1, "%s", errbuf
);
211 if ((ut
= fopen(_PATH_UTMP
, "r")) == NULL
)
212 err(1, "%s", _PATH_UTMP
);
217 for (nusers
= 0; fread(&utmp
, sizeof(utmp
), 1, ut
);) {
218 if (utmp
.ut_name
[0] == '\0')
220 if (!(stp
= ttystat(utmp
.ut_line
, UT_LINESIZE
)))
221 continue; /* corrupted record */
230 for (user
= sel_users
; !usermatch
&& *user
; user
++)
231 if (!strncmp(utmp
.ut_name
, *user
, UT_NAMESIZE
))
236 if ((ep
= calloc(1, sizeof(struct entry
))) == NULL
)
240 memmove(&ep
->utmp
, &utmp
, sizeof(struct utmp
));
241 ep
->tdev
= stp
->st_rdev
;
243 * If this is the console device, attempt to ascertain
244 * the true console device dev_t.
249 size
= sizeof(dev_t
);
250 (void)sysctlbyname("machdep.consdev", &ep
->tdev
, &size
, NULL
, 0);
252 touched
= stp
->st_atime
;
253 if (touched
< ep
->utmp
.ut_time
) {
254 /* tty untouched since before login */
255 touched
= ep
->utmp
.ut_time
;
257 if ((ep
->idle
= now
- touched
) < 0)
262 if (header
|| wcmd
== 0) {
263 pr_header(&now
, nusers
);
269 #define HEADER_USER "USER"
270 #define HEADER_TTY "TTY"
271 #define HEADER_FROM "FROM"
272 #define HEADER_LOGIN_IDLE "LOGIN@ IDLE "
273 #define HEADER_WHAT "WHAT\n"
274 #define WUSED (UT_NAMESIZE + UT_LINESIZE + W_DISPHOSTSIZE + \
275 sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */
276 (void)printf("%-*.*s %-*.*s %-*.*s %s",
277 UT_NAMESIZE
, UT_NAMESIZE
, HEADER_USER
,
278 UT_LINESIZE
, UT_LINESIZE
, HEADER_TTY
,
279 W_DISPHOSTSIZE
, W_DISPHOSTSIZE
, HEADER_FROM
,
280 HEADER_LOGIN_IDLE HEADER_WHAT
);
283 if ((kp
= kvm_getprocs(kd
, KERN_PROC_ALL
, 0, &nentries
)) == NULL
)
284 err(1, "%s", kvm_geterr(kd
));
285 for (i
= 0; i
< nentries
; i
++, kp
++) {
286 if (kp
->ki_stat
== SIDL
|| kp
->ki_stat
== SZOMB
)
288 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
289 if (ep
->tdev
== kp
->ki_tdev
) {
291 * proc is associated with this terminal
293 if (ep
->kp
== NULL
&& kp
->ki_pgid
== kp
->ki_tpgid
) {
295 * Proc is 'most interesting'
297 if (proc_compare(ep
->kp
, kp
))
301 * Proc debug option info; add to debug
302 * list using kinfo_proc ki_spare[0]
303 * as next pointer; ptr to ptr avoids the
304 * ptr = long assumption.
312 if ((ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
313 ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
314 ioctl(STDIN_FILENO
, TIOCGWINSZ
, &ws
) == -1) || ws
.ws_col
== 0)
317 ttywidth
= ws
.ws_col
- 1;
318 argwidth
= ttywidth
- WUSED
;
321 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
322 if (ep
->kp
== NULL
) {
323 ep
->args
= strdup("-");
326 ep
->args
= fmt_argv(kvm_getargv(kd
, ep
->kp
, argwidth
),
327 ep
->kp
->ki_comm
, MAXCOMLEN
);
328 if (ep
->args
== NULL
)
331 /* sort by idle time */
332 if (sortidle
&& ehead
!= NULL
) {
333 struct entry
*from
, *save
;
337 while (from
!= NULL
) {
339 (*nextp
) && from
->idle
>= (*nextp
)->idle
;
340 nextp
= &(*nextp
)->next
)
349 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
350 char host_buf
[UT_HOSTSIZE
+ 1];
351 struct sockaddr_storage ss
;
352 struct sockaddr
*sa
= (struct sockaddr
*)&ss
;
353 struct sockaddr_in
*lsin
= (struct sockaddr_in
*)&ss
;
354 struct sockaddr_in6
*lsin6
= (struct sockaddr_in6
*)&ss
;
358 host_buf
[UT_HOSTSIZE
] = '\0';
359 strncpy(host_buf
, ep
->utmp
.ut_host
, UT_HOSTSIZE
);
360 p
= *host_buf
? host_buf
: "-";
361 if ((x_suffix
= strrchr(p
, ':')) != NULL
) {
362 if ((dot
= strchr(x_suffix
, '.')) != NULL
&&
363 strchr(dot
+1, '.') == NULL
)
369 /* Attempt to change an IP address into a name */
371 memset(&ss
, '\0', sizeof(ss
));
372 if (inet_pton(AF_INET6
, p
, &lsin6
->sin6_addr
) == 1) {
373 lsin6
->sin6_len
= sizeof(*lsin6
);
374 lsin6
->sin6_family
= AF_INET6
;
376 } else if (inet_pton(AF_INET
, p
, &lsin
->sin_addr
) == 1) {
377 lsin
->sin_len
= sizeof(*lsin
);
378 lsin
->sin_family
= AF_INET
;
381 if (isaddr
&& realhostname_sa(fn
, sizeof(fn
), sa
,
382 sa
->sa_len
) == HOSTNAME_FOUND
)
386 (void)snprintf(buf
, sizeof(buf
), "%s:%s", p
, x_suffix
);
390 for (dkp
= ep
->dkp
; dkp
!= NULL
; dkp
= debugproc(dkp
)) {
393 ptr
= fmt_argv(kvm_getargv(kd
, dkp
, argwidth
),
394 dkp
->ki_comm
, MAXCOMLEN
);
397 (void)printf("\t\t%-9d %s\n",
401 (void)printf("%-*.*s %-*.*s %-*.*s ",
402 UT_NAMESIZE
, UT_NAMESIZE
, ep
->utmp
.ut_name
,
403 UT_LINESIZE
, UT_LINESIZE
,
404 strncmp(ep
->utmp
.ut_line
, "tty", 3) &&
405 strncmp(ep
->utmp
.ut_line
, "cua", 3) ?
406 ep
->utmp
.ut_line
: ep
->utmp
.ut_line
+ 3,
407 W_DISPHOSTSIZE
, W_DISPHOSTSIZE
, *p
? p
: "-");
408 t
= _time_to_time32(ep
->utmp
.ut_time
);
410 longidle
= pr_idle(ep
->idle
);
411 (void)printf("%.*s\n", argwidth
- longidle
, ep
->args
);
418 pr_header(time_t *nowp
, int nusers
)
423 int days
, hrs
, i
, mins
, secs
;
429 if (strftime(buf
, sizeof(buf
),
430 use_ampm
? "%l:%M%p" : "%k:%M", localtime(nowp
)) != 0)
431 (void)printf("%s ", buf
);
433 * Print how long system has been up.
435 if (clock_gettime(CLOCK_MONOTONIC
, &tp
) != -1) {
439 days
= uptime
/ 86400;
447 (void)printf(" %d day%s,", days
, days
> 1 ? "s" : "");
448 if (hrs
> 0 && mins
> 0)
449 (void)printf(" %2d:%02d,", hrs
, mins
);
451 (void)printf(" %d hr%s,", hrs
, hrs
> 1 ? "s" : "");
453 (void)printf(" %d min%s,", mins
, mins
> 1 ? "s" : "");
455 (void)printf(" %d sec%s,", secs
, secs
> 1 ? "s" : "");
458 /* Print number of users logged in to system */
459 (void)printf(" %d user%s", nusers
, nusers
== 1 ? "" : "s");
462 * Print 1, 5, and 15 minute load averages.
464 if (getloadavg(avenrun
, sizeof(avenrun
) / sizeof(avenrun
[0])) == -1)
465 (void)printf(", no load average information available\n");
467 (void)printf(", load averages:");
468 for (i
= 0; i
< (int)(sizeof(avenrun
) / sizeof(avenrun
[0])); i
++) {
469 if (use_comma
&& i
> 0)
471 (void)printf(" %.2f", avenrun
[i
]);
478 ttystat(char *line
, int sz
)
480 static struct stat sb
;
481 char ttybuf
[MAXPATHLEN
];
483 (void)snprintf(ttybuf
, sizeof(ttybuf
), "%s%.*s", _PATH_DEV
, sz
, line
);
484 if (stat(ttybuf
, &sb
) == 0) {
494 (void)fprintf(stderr
,
495 "usage: w [-dhin] [-M core] [-N system] [user ...]\n");
497 (void)fprintf(stderr
, "usage: uptime\n");
502 this_is_uptime(const char *s
)
506 if ((u
= strrchr(s
, '/')) != NULL
)
510 if (strcmp(u
, "uptime") == 0)