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 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * @(#) Copyright (c) 1980, 1991, 1993, 1994 The Regents of the University of California. All rights reserved.
30 * @(#)w.c 8.4 (Berkeley) 4/16/94
31 * $FreeBSD: src/usr.bin/w/w.c,v 1.38.2.6 2002/03/12 19:51:51 phantom Exp $
32 * $DragonFly: src/usr.bin/w/w.c,v 1.10 2007/02/18 16:15:24 corecode Exp $
36 * w - print system status (who and what)
38 * This program is similar to the systat command on Tenex/Tops 10/20
42 #include <sys/param.h>
45 #include <sys/sysctl.h>
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
50 #include <machine/cpu.h>
51 #include <netinet/in.h>
52 #include <arpa/inet.h>
77 #include <arpa/nameser.h>
82 struct timeval boottime
;
88 time_t now
; /* the current time of day */
90 time_t uptime
; /* time of last reboot & elapsed time since */
91 int ttywidth
; /* width of tty */
92 int argwidth
; /* width of tty */
93 int header
= 1; /* true if -h flag: don't print heading */
94 int nflag
; /* true if -n flag: don't convert addrs */
95 int dflag
; /* true if -d flag: output debug info */
96 int sortidle
; /* sort by idle time */
97 int use_ampm
; /* use AM/PM time */
98 int use_comma
; /* use comma as floats separator */
99 char **sel_users
; /* login array of particular users selected */
100 char domain
[MAXHOSTNAMELEN
];
101 int maxname
= 8, maxline
= 3, maxhost
= 16;
104 * One of these per active utmp entry.
108 char name
[UTX_USERSIZE
+ 1];
109 char line
[UTX_LINESIZE
+ 1];
110 char host
[UTX_HOSTSIZE
+ 1];
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 pid_t pid
; /* pid or ~0 if not known */
119 } *ep
, *ehead
= NULL
, **nextp
= &ehead
;
121 #define debugproc(p) *((struct kinfo_proc **)&(p)->kp_spare[0])
123 static void pr_header(time_t *, int);
124 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
125 static struct stat
*ttystat(char *, int);
126 static void process(struct entry
*);
128 static void usage(int);
129 static int this_is_uptime(const char *s
);
131 char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */
134 main(int argc
, char **argv
)
136 struct kinfo_proc
*kp
;
137 struct kinfo_proc
*dkp
;
140 int ch
, i
, nentries
, nusers
, wcmd
, longidle
, dropgid
;
141 char *memf
, *nlistf
, *p
, *x
;
148 char buf
[MAXHOSTNAMELEN
], errbuf
[_POSIX2_LINE_MAX
];
150 (void)setlocale(LC_ALL
, "");
151 use_ampm
= (*nl_langinfo(T_FMT_AMPM
) != '\0');
152 use_comma
= (*nl_langinfo(RADIXCHAR
) != ',');
154 /* Are we w(1) or uptime(1)? */
155 if (this_is_uptime(argv
[0]) == 0) {
164 memf
= nlistf
= _PATH_DEVNULL
;
165 while ((ch
= getopt(argc
, argv
, p
)) != -1)
188 case 'f': case 'l': case 's': case 'u': case 'w':
189 warnx("[-flsuw] no longer supported");
198 if (!(_res
.options
& RES_INIT
))
200 _res
.retrans
= 2; /* resolver timeout to 2 seconds per try */
201 _res
.retry
= 1; /* only try once.. */
204 * Discard setgid privileges if not the running kernel so that bad
205 * guys can't print interesting stuff from kernel memory.
210 if ((kd
= kvm_openfiles(nlistf
, memf
, NULL
, O_RDONLY
, errbuf
)) == NULL
)
211 errx(1, "%s", errbuf
);
226 while ((utx
= getutxent()) != NULL
) {
227 if (utx
->ut_type
!= USER_PROCESS
)
236 for (user
= sel_users
; !usermatch
&& *user
; user
++)
237 if (!strncmp(utx
->ut_name
, *user
, UTX_USERSIZE
))
243 if ((ep
= calloc(1, sizeof(struct entry
))) == NULL
)
245 (void)memcpy(ep
->name
, utx
->ut_name
, sizeof(utx
->ut_name
));
246 (void)memcpy(ep
->line
, utx
->ut_line
, sizeof(utx
->ut_line
));
247 (void)memcpy(ep
->host
, utx
->ut_host
, sizeof(utx
->ut_host
));
248 ep
->name
[sizeof(utx
->ut_name
)] = '\0';
249 ep
->line
[sizeof(utx
->ut_line
)] = '\0';
250 ep
->host
[sizeof(utx
->ut_host
)] = '\0';
252 /* XXX: Actually we don't support the utx->ut_ss stuff yet */
253 if (!nflag
|| getnameinfo((struct sockaddr
*)&utx
->ut_ss
,
254 utx
->ut_ss
.ss_len
, ep
->host
, sizeof(ep
->host
), NULL
, 0,
255 NI_NUMERICHOST
) != 0) {
256 (void)memcpy(ep
->host
, utx
->ut_host
,
257 sizeof(utx
->ut_host
));
258 ep
->host
[sizeof(utx
->ut_host
)] = '\0';
263 ep
->pid
= utx
->ut_pid
;
273 while ((ut
= getutent()) != NULL
) {
274 if (ut
->ut_name
[0] == '\0')
282 for (user
= sel_users
; !usermatch
&& *user
; user
++)
283 if (!strncmp(ut
->ut_name
, *user
, UT_NAMESIZE
))
289 /* Don't process entries that we have utmpx for */
290 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
291 if (strncmp(ep
->line
, ut
->ut_line
,
292 sizeof(ut
->ut_line
)) == 0)
296 --nusers
; /* Duplicate entry */
300 if ((ep
= calloc(1, sizeof(struct entry
))) == NULL
)
302 (void)memcpy(ep
->name
, ut
->ut_name
, sizeof(ut
->ut_name
));
303 (void)memcpy(ep
->line
, ut
->ut_line
, sizeof(ut
->ut_line
));
304 (void)memcpy(ep
->host
, ut
->ut_host
, sizeof(ut
->ut_host
));
305 ep
->name
[sizeof(ut
->ut_name
)] = '\0';
306 ep
->line
[sizeof(ut
->ut_line
)] = '\0';
307 ep
->host
[sizeof(ut
->ut_host
)] = '\0';
308 ep
->tv
.tv_sec
= ut
->ut_time
;
324 if (header
|| wcmd
== 0) {
325 pr_header(&now
, nusers
);
331 #define HEADER_USER "USER"
332 #define HEADER_TTY "TTY"
333 #define HEADER_FROM "FROM"
334 #define HEADER_LOGIN_IDLE "LOGIN@ IDLE "
335 #define HEADER_WHAT "WHAT\n"
336 #define WUSED (maxname + maxline + maxhost + \
337 sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */
338 (void)printf("%-*.*s %-*.*s %-*.*s %s",
339 maxname
, maxname
, HEADER_USER
,
340 maxline
, maxline
, HEADER_TTY
,
341 maxhost
, maxhost
, HEADER_FROM
,
342 HEADER_LOGIN_IDLE HEADER_WHAT
);
345 if ((kp
= kvm_getprocs(kd
, KERN_PROC_ALL
, 0, &nentries
)) == NULL
)
346 err(1, "%s", kvm_geterr(kd
));
347 for (i
= 0; i
< nentries
; i
++, kp
++) {
348 if (kp
->kp_stat
== SIDL
|| kp
->kp_stat
== SZOMB
)
350 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
351 if (ep
->tdev
== kp
->kp_tdev
) {
353 * proc is associated with this terminal
355 if (ep
->kp
== NULL
&& kp
->kp_pgid
== kp
->kp_tpgid
) {
357 * Proc is 'most interesting'
359 if (proc_compare(ep
->kp
, kp
))
363 * Proc debug option info; add to debug
364 * list using kinfo_proc kp_eproc.e_spare
365 * as next pointer; ptr to ptr avoids the
366 * ptr = long assumption.
372 if (ep
->pid
!= 0 && ep
->pid
== kp
->kp_pid
) {
378 if ((ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
379 ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
380 ioctl(STDIN_FILENO
, TIOCGWINSZ
, &ws
) == -1) || ws
.ws_col
== 0)
383 ttywidth
= ws
.ws_col
- 1;
384 argwidth
= ttywidth
- WUSED
;
387 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
388 if (ep
->kp
== NULL
) {
392 ep
->args
= fmt_argv(kvm_getargv(kd
, ep
->kp
, argwidth
),
393 ep
->kp
->kp_comm
, MAXCOMLEN
);
394 if (ep
->args
== NULL
)
397 /* sort by idle time */
398 if (sortidle
&& ehead
!= NULL
) {
399 struct entry
*from
, *save
;
403 while (from
!= NULL
) {
405 (*nextp
) && from
->idle
>= (*nextp
)->idle
;
406 nextp
= &(*nextp
)->next
)
414 #if defined(SUPPORT_UTMP) && defined(SUPPORT_UTMPX)
415 else if (ehead
!= NULL
) {
416 struct entry
*from
= ehead
, *save
;
419 while (from
!= NULL
) {
421 (*nextp
) && strcmp(from
->line
, (*nextp
)->line
) > 0;
422 nextp
= &(*nextp
)->next
)
433 if (gethostname(domain
, sizeof(domain
)) < 0 ||
434 (p
= strchr(domain
, '.')) == NULL
)
437 domain
[sizeof(domain
) - 1] = '\0';
438 memmove(domain
, p
, strlen(p
) + 1);
442 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
443 char host_buf
[UTX_HOSTSIZE
+ 1];
445 host_buf
[UTX_HOSTSIZE
] = '\0';
446 strncpy(host_buf
, ep
->host
, maxhost
);
447 p
= *host_buf
? host_buf
: "-";
448 if ((x
= strchr(p
, ':')) != NULL
)
450 if (!nflag
&& isdigit(*p
) && (l
= inet_addr(p
)) != -1 &&
451 (hp
= gethostbyaddr(&l
, sizeof(l
), AF_INET
))) {
452 if (domain
[0] != '\0') {
454 p
+= strlen(hp
->h_name
);
456 if (p
> hp
->h_name
&&
457 strcasecmp(p
, domain
) == 0)
462 if (nflag
&& *p
&& strcmp(p
, "-") &&
463 inet_addr(p
) == INADDR_NONE
) {
464 hp
= gethostbyname(p
);
469 memmove(&in
, hp
->h_addr
, sizeof(in
));
474 (void)snprintf(buf
, sizeof(buf
), "%s:%s", p
, x
);
478 for (dkp
= ep
->dkp
; dkp
!= NULL
; dkp
= debugproc(dkp
)) {
481 ptr
= fmt_argv(kvm_getargv(kd
, dkp
, argwidth
),
482 dkp
->kp_comm
, MAXCOMLEN
);
485 (void)printf("\t\t%-9d %s\n",
489 (void)printf("%-*.*s %-*.*s %-*.*s ",
490 maxname
, maxname
, ep
->name
,
492 strncmp(ep
->line
, "tty", 3) &&
493 strncmp(ep
->line
, "cua", 3) ?
494 ep
->line
: ep
->line
+ 3,
495 maxhost
, maxhost
, *p
? p
: "-");
496 then
= (time_t)ep
->tv
.tv_sec
;
497 pr_attime(&then
, &now
);
498 longidle
= pr_idle(ep
->idle
);
499 (void)printf("%.*s\n", argwidth
- longidle
, ep
->args
);
506 pr_header(time_t *nowp
, int nusers
)
510 int days
, hrs
, i
, mins
, secs
;
518 (void)strftime(buf
, sizeof(buf
) - 1,
519 use_ampm
? "%l:%M%p" : "%k:%M", localtime(nowp
));
520 buf
[sizeof(buf
) - 1] = '\0';
521 (void)printf("%s ", buf
);
524 * Print how long system has been up.
525 * (Found by looking at "boottime" from the kernel)
528 mib
[1] = KERN_BOOTTIME
;
529 size
= sizeof(boottime
);
530 if (sysctl(mib
, 2, &boottime
, &size
, NULL
, 0) != -1 &&
531 boottime
.tv_sec
!= 0) {
532 uptime
= now
- boottime
.tv_sec
;
535 days
= uptime
/ 86400;
543 (void)printf(" %d day%s,", days
, days
> 1 ? "s" : "");
544 if (hrs
> 0 && mins
> 0)
545 (void)printf(" %2d:%02d,", hrs
, mins
);
547 (void)printf(" %d hr%s,", hrs
, hrs
> 1 ? "s" : "");
549 (void)printf(" %d min%s,", mins
, mins
> 1 ? "s" : "");
551 (void)printf(" %d sec%s,", secs
, secs
> 1 ? "s" : "");
554 /* Print number of users logged in on system */
555 (void)printf(" %d user%s", nusers
, nusers
== 1 ? "" : "s");
558 * Print 1, 5, and 15 minute load averages.
560 if (getloadavg(avenrun
, sizeof(avenrun
) / sizeof(avenrun
[0])) == -1)
561 (void)printf(", no load average information available\n");
563 (void)printf(", load averages:");
564 for (i
= 0; i
< (sizeof(avenrun
) / sizeof(avenrun
[0])); i
++) {
565 if (use_comma
&& i
> 0)
567 (void)printf(" %.2f", avenrun
[i
]);
574 ttystat(char *line
, int sz
)
576 static struct stat sb
;
577 char ttybuf
[MAXPATHLEN
];
579 (void)snprintf(ttybuf
, sizeof(ttybuf
), "%s%.*s", _PATH_DEV
, sz
, line
);
580 if (stat(ttybuf
, &sb
)) {
591 (void)fprintf(stderr
,
592 "usage: w [-dhin] [-M core] [-N system] [user ...]\n");
594 (void)fprintf(stderr
, "usage: uptime\n");
599 this_is_uptime(const char *s
)
603 if ((u
= strrchr(s
, '/')) != NULL
)
607 if (strcmp(u
, "uptime") == 0)
612 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
614 process(struct entry
*ep
)
620 if ((max
= strlen(ep
->name
)) > maxname
)
622 if ((max
= strlen(ep
->line
)) > maxline
)
624 if ((max
= strlen(ep
->host
)) > maxhost
)
628 ep
->idle
= (time_t)-1;
630 if (!(stp
= ttystat(ep
->line
, maxline
)))
631 return; /* corrupted record */
633 ep
->tdev
= stp
->st_rdev
;
636 * If this is the console device, attempt to ascertain
637 * the true console device dev_t.
643 mib
[0] = CTL_MACHDEP
;
644 mib
[1] = CPU_CONSDEV
;
645 size
= sizeof(dev_t
);
646 (void)sysctl(mib
, 2, &ep
->tdev
, &size
, NULL
, 0);
650 touched
= stp
->st_atime
;
651 if (touched
< ep
->tv
.tv_sec
) {
652 /* tty untouched since before login */
653 touched
= ep
->tv
.tv_sec
;
655 if ((ep
->idle
= now
- touched
) < 0)