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
33 * @(#) Copyright (c) 1980, 1991, 1993, 1994 The Regents of the University of California. All rights reserved.
34 * @(#)w.c 8.4 (Berkeley) 4/16/94
35 * $FreeBSD: src/usr.bin/w/w.c,v 1.38.2.6 2002/03/12 19:51:51 phantom Exp $
36 * $DragonFly: src/usr.bin/w/w.c,v 1.10 2007/02/18 16:15:24 corecode Exp $
40 * w - print system status (who and what)
42 * This program is similar to the systat command on Tenex/Tops 10/20
45 #include <sys/param.h>
48 #include <sys/sysctl.h>
50 #include <sys/ioctl.h>
51 #include <sys/socket.h>
54 #include <machine/cpu.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
76 #include <arpa/nameser.h>
81 struct timeval boottime
;
85 time_t now
; /* the current time of day */
86 time_t uptime
; /* time of last reboot & elapsed time since */
87 int ttywidth
; /* width of tty */
88 int argwidth
; /* width of tty */
89 int header
= 1; /* true if -h flag: don't print heading */
90 int nflag
; /* true if -n flag: don't convert addrs */
91 int dflag
; /* true if -d flag: output debug info */
92 int sortidle
; /* sort by idle time */
93 int use_ampm
; /* use AM/PM time */
94 int use_comma
; /* use comma as floats separator */
95 char **sel_users
; /* login array of particular users selected */
96 char domain
[MAXHOSTNAMELEN
];
99 * One of these per active utmp entry.
104 dev_t tdev
; /* dev_t of terminal */
105 time_t idle
; /* idle time of terminal in seconds */
106 struct kinfo_proc
*kp
; /* `most interesting' proc */
107 char *args
; /* arg list of interesting process */
108 struct kinfo_proc
*dkp
; /* debug option proc list */
109 } *ep
, *ehead
= NULL
, **nextp
= &ehead
;
111 #define debugproc(p) *((struct kinfo_proc **)&(p)->kp_spare[0])
113 static void pr_header(time_t *, int);
114 static struct stat
*ttystat(char *, int);
115 static void usage(int);
116 static int this_is_uptime(const char *s
);
118 char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */
121 main(int argc
, char **argv
)
123 struct kinfo_proc
*kp
;
124 struct kinfo_proc
*dkp
;
130 int ch
, i
, nentries
, nusers
, wcmd
, longidle
, dropgid
;
131 char *memf
, *nlistf
, *p
, *x
;
132 char buf
[MAXHOSTNAMELEN
], errbuf
[_POSIX2_LINE_MAX
];
134 (void)setlocale(LC_ALL
, "");
135 use_ampm
= (*nl_langinfo(T_FMT_AMPM
) != '\0');
136 use_comma
= (*nl_langinfo(RADIXCHAR
) != ',');
138 /* Are we w(1) or uptime(1)? */
139 if (this_is_uptime(argv
[0]) == 0) {
148 memf
= nlistf
= _PATH_DEVNULL
;
149 while ((ch
= getopt(argc
, argv
, p
)) != -1)
172 case 'f': case 'l': case 's': case 'u': case 'w':
173 warnx("[-flsuw] no longer supported");
182 if (!(_res
.options
& RES_INIT
))
184 _res
.retrans
= 2; /* resolver timeout to 2 seconds per try */
185 _res
.retry
= 1; /* only try once.. */
188 * Discard setgid privileges if not the running kernel so that bad
189 * guys can't print interesting stuff from kernel memory.
194 if ((kd
= kvm_openfiles(nlistf
, memf
, NULL
, O_RDONLY
, errbuf
)) == NULL
)
195 errx(1, "%s", errbuf
);
198 if ((ut
= fopen(_PATH_UTMP
, "r")) == NULL
)
199 err(1, "%s", _PATH_UTMP
);
204 for (nusers
= 0; fread(&utmp
, sizeof(utmp
), 1, ut
);) {
205 if (utmp
.ut_name
[0] == '\0')
207 if (!(stp
= ttystat(utmp
.ut_line
, UT_LINESIZE
)))
208 continue; /* corrupted record */
217 for (user
= sel_users
; !usermatch
&& *user
; user
++)
218 if (!strncmp(utmp
.ut_name
, *user
, UT_NAMESIZE
))
223 if ((ep
= calloc(1, sizeof(struct entry
))) == NULL
)
227 memmove(&ep
->utmp
, &utmp
, sizeof(struct utmp
));
228 ep
->tdev
= stp
->st_rdev
;
231 * If this is the console device, attempt to ascertain
232 * the true console device dev_t.
238 mib
[0] = CTL_MACHDEP
;
239 mib
[1] = CPU_CONSDEV
;
240 size
= sizeof(dev_t
);
241 (void)sysctl(mib
, 2, &ep
->tdev
, &size
, NULL
, 0);
244 touched
= stp
->st_atime
;
245 if (touched
< ep
->utmp
.ut_time
) {
246 /* tty untouched since before login */
247 touched
= ep
->utmp
.ut_time
;
249 if ((ep
->idle
= now
- touched
) < 0)
254 if (header
|| wcmd
== 0) {
255 pr_header(&now
, nusers
);
261 #define HEADER_USER "USER"
262 #define HEADER_TTY "TTY"
263 #define HEADER_FROM "FROM"
264 #define HEADER_LOGIN_IDLE "LOGIN@ IDLE "
265 #define HEADER_WHAT "WHAT\n"
266 #define WUSED (UT_NAMESIZE + UT_LINESIZE + UT_HOSTSIZE + \
267 sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */
268 (void)printf("%-*.*s %-*.*s %-*.*s %s",
269 UT_NAMESIZE
, UT_NAMESIZE
, HEADER_USER
,
270 UT_LINESIZE
, UT_LINESIZE
, HEADER_TTY
,
271 UT_HOSTSIZE
, UT_HOSTSIZE
, HEADER_FROM
,
272 HEADER_LOGIN_IDLE HEADER_WHAT
);
275 if ((kp
= kvm_getprocs(kd
, KERN_PROC_ALL
, 0, &nentries
)) == NULL
)
276 err(1, "%s", kvm_geterr(kd
));
277 for (i
= 0; i
< nentries
; i
++, kp
++) {
278 if (kp
->kp_stat
== SIDL
|| kp
->kp_stat
== SZOMB
)
280 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
281 if (ep
->tdev
== kp
->kp_tdev
) {
283 * proc is associated with this terminal
285 if (ep
->kp
== NULL
&& kp
->kp_pgid
== kp
->kp_tpgid
) {
287 * Proc is 'most interesting'
289 if (proc_compare(ep
->kp
, kp
))
293 * Proc debug option info; add to debug
294 * list using kinfo_proc kp_eproc.e_spare
295 * as next pointer; ptr to ptr avoids the
296 * ptr = long assumption.
304 if ((ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
305 ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
306 ioctl(STDIN_FILENO
, TIOCGWINSZ
, &ws
) == -1) || ws
.ws_col
== 0)
309 ttywidth
= ws
.ws_col
- 1;
310 argwidth
= ttywidth
- WUSED
;
313 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
314 if (ep
->kp
== NULL
) {
318 ep
->args
= fmt_argv(kvm_getargv(kd
, ep
->kp
, argwidth
),
319 ep
->kp
->kp_comm
, MAXCOMLEN
);
320 if (ep
->args
== NULL
)
323 /* sort by idle time */
324 if (sortidle
&& ehead
!= NULL
) {
325 struct entry
*from
, *save
;
329 while (from
!= NULL
) {
331 (*nextp
) && from
->idle
>= (*nextp
)->idle
;
332 nextp
= &(*nextp
)->next
)
342 if (gethostname(domain
, sizeof(domain
)) < 0 ||
343 (p
= strchr(domain
, '.')) == NULL
)
346 domain
[sizeof(domain
) - 1] = '\0';
347 memmove(domain
, p
, strlen(p
) + 1);
351 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
352 char host_buf
[UT_HOSTSIZE
+ 1];
354 host_buf
[UT_HOSTSIZE
] = '\0';
355 strncpy(host_buf
, ep
->utmp
.ut_host
, UT_HOSTSIZE
);
356 p
= *host_buf
? host_buf
: "-";
357 if ((x
= strchr(p
, ':')) != NULL
)
359 if (!nflag
&& isdigit(*p
) && (l
= inet_addr(p
)) != -1 &&
360 (hp
= gethostbyaddr((char *)&l
, sizeof(l
), AF_INET
))) {
361 if (domain
[0] != '\0') {
363 p
+= strlen(hp
->h_name
);
365 if (p
> hp
->h_name
&&
366 strcasecmp(p
, domain
) == 0)
371 if (nflag
&& *p
&& strcmp(p
, "-") &&
372 inet_addr(p
) == INADDR_NONE
) {
373 hp
= gethostbyname(p
);
378 memmove(&in
, hp
->h_addr
, sizeof(in
));
383 (void)snprintf(buf
, sizeof(buf
), "%s:%s", p
, x
);
387 for (dkp
= ep
->dkp
; dkp
!= NULL
; dkp
= debugproc(dkp
)) {
390 ptr
= fmt_argv(kvm_getargv(kd
, dkp
, argwidth
),
391 dkp
->kp_comm
, MAXCOMLEN
);
394 (void)printf("\t\t%-9d %s\n",
398 (void)printf("%-*.*s %-*.*s %-*.*s ",
399 UT_NAMESIZE
, UT_NAMESIZE
, ep
->utmp
.ut_name
,
400 UT_LINESIZE
, UT_LINESIZE
,
401 strncmp(ep
->utmp
.ut_line
, "tty", 3) &&
402 strncmp(ep
->utmp
.ut_line
, "cua", 3) ?
403 ep
->utmp
.ut_line
: ep
->utmp
.ut_line
+ 3,
404 UT_HOSTSIZE
, UT_HOSTSIZE
, *p
? p
: "-");
405 pr_attime(&ep
->utmp
.ut_time
, &now
);
406 longidle
= pr_idle(ep
->idle
);
407 (void)printf("%.*s\n", argwidth
- longidle
, ep
->args
);
414 pr_header(time_t *nowp
, int nusers
)
418 int days
, hrs
, i
, mins
, secs
;
426 (void)strftime(buf
, sizeof(buf
) - 1,
427 use_ampm
? "%l:%M%p" : "%k:%M", localtime(nowp
));
428 buf
[sizeof(buf
) - 1] = '\0';
429 (void)printf("%s ", buf
);
432 * Print how long system has been up.
433 * (Found by looking at "boottime" from the kernel)
436 mib
[1] = KERN_BOOTTIME
;
437 size
= sizeof(boottime
);
438 if (sysctl(mib
, 2, &boottime
, &size
, NULL
, 0) != -1 &&
439 boottime
.tv_sec
!= 0) {
440 uptime
= now
- boottime
.tv_sec
;
443 days
= uptime
/ 86400;
451 (void)printf(" %d day%s,", days
, days
> 1 ? "s" : "");
452 if (hrs
> 0 && mins
> 0)
453 (void)printf(" %2d:%02d,", hrs
, mins
);
455 (void)printf(" %d hr%s,", hrs
, hrs
> 1 ? "s" : "");
457 (void)printf(" %d min%s,", mins
, mins
> 1 ? "s" : "");
459 (void)printf(" %d sec%s,", secs
, secs
> 1 ? "s" : "");
462 /* Print number of users logged in on system */
463 (void)printf(" %d user%s", nusers
, nusers
== 1 ? "" : "s");
466 * Print 1, 5, and 15 minute load averages.
468 if (getloadavg(avenrun
, sizeof(avenrun
) / sizeof(avenrun
[0])) == -1)
469 (void)printf(", no load average information available\n");
471 (void)printf(", load averages:");
472 for (i
= 0; i
< (sizeof(avenrun
) / sizeof(avenrun
[0])); i
++) {
473 if (use_comma
&& i
> 0)
475 (void)printf(" %.2f", avenrun
[i
]);
482 ttystat(char *line
, int sz
)
484 static struct stat sb
;
485 char ttybuf
[MAXPATHLEN
];
487 (void)snprintf(ttybuf
, sizeof(ttybuf
), "%s%.*s", _PATH_DEV
, sz
, line
);
488 if (stat(ttybuf
, &sb
)) {
499 (void)fprintf(stderr
,
500 "usage: w [-dhin] [-M core] [-N system] [user ...]\n");
502 (void)fprintf(stderr
, "usage: uptime\n");
507 this_is_uptime(const char *s
)
511 if ((u
= strrchr(s
, '/')) != NULL
)
515 if (strcmp(u
, "uptime") == 0)