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>
37 static const char copyright
[] =
38 "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n";
43 static const char sccsid
[] = "@(#)w.c 8.4 (Berkeley) 4/16/94";
47 * w - print system status (who and what)
49 * This program is similar to the systat command on Tenex/Tops 10/20
52 #include <sys/param.h>
55 #include <sys/sysctl.h>
58 #include <sys/ioctl.h>
59 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #include <arpa/nameser.h>
86 struct timeval boottime
;
90 time_t now
; /* the current time of day */
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 */
102 * One of these per active utmp entry.
107 dev_t tdev
; /* dev_t of terminal */
108 time_t idle
; /* idle time of terminal in seconds */
109 struct kinfo_proc
*kp
; /* `most interesting' proc */
110 char *args
; /* arg list of interesting process */
111 struct kinfo_proc
*dkp
; /* debug option proc list */
112 } *ep
, *ehead
= NULL
, **nextp
= &ehead
;
114 #define debugproc(p) *((struct kinfo_proc **)&(p)->ki_spare[0])
116 /* W_DISPHOSTSIZE should not be greater than UT_HOSTSIZE */
117 #define W_DISPHOSTSIZE 16
119 static void pr_header(time_t *, int);
120 static struct stat
*ttystat(char *, int);
121 static void usage(int);
122 static int this_is_uptime(const char *s
);
123 static void w_getargv(void);
125 char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */
132 struct kinfo_proc
*kp
, *kprocbuf
;
133 struct kinfo_proc
*dkp
;
137 int ch
, i
, nentries
, nusers
, wcmd
, longidle
, dropgid
;
138 const char *memf
, *nlistf
, *p
;
140 char buf
[MAXHOSTNAMELEN
], errbuf
[_POSIX2_LINE_MAX
];
141 char fn
[MAXHOSTNAMELEN
];
143 int local_error
= 0, retry_count
= 0;
145 size_t orig_bufSize
= 0;
146 int mib
[4] = {CTL_KERN
, KERN_PROC
, KERN_PROC_ALL
, 0 };
148 (void)setlocale(LC_ALL
, "");
150 use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0');
151 use_comma = (*nl_langinfo(RADIXCHAR) != ',');
154 /* Are we w(1) or uptime(1)? */
155 if (this_is_uptime(argv
[0]) == 0) {
164 memf
= nlistf
= NULL
;
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.
211 if ((kd
= kvm_openfiles(nlistf
, memf
, NULL
, O_RDONLY
, errbuf
)) == NULL
)
212 errx(1, "%s", errbuf
);
216 if ((ut
= fopen(_PATH_UTMP
, "r")) == NULL
)
217 err(1, "%s", _PATH_UTMP
);
222 for (nusers
= 0; fread(&utmp
, sizeof(utmp
), 1, ut
);) {
223 if (utmp
.ut_name
[0] == '\0')
225 if (!(stp
= ttystat(utmp
.ut_line
, UT_LINESIZE
)))
226 continue; /* corrupted record */
235 for (user
= sel_users
; !usermatch
&& *user
; user
++)
236 if (!strncmp(utmp
.ut_name
, *user
, UT_NAMESIZE
))
241 if ((ep
= calloc(1, sizeof(struct entry
))) == NULL
)
245 memmove(&ep
->utmp
, &utmp
, sizeof(struct utmp
));
246 ep
->tdev
= stp
->st_rdev
;
249 * If this is the console device, attempt to ascertain
250 * the true console device dev_t.
256 mib
[0] = CTL_MACHDEP
;
257 mib
[1] = CPU_CONSDEV
;
258 size
= sizeof(dev_t
);
259 (void)sysctl(mib
, 2, &ep
->tdev
, &size
, NULL
, 0);
262 touched
= stp
->st_atime
;
263 if (touched
< ep
->utmp
.ut_time
) {
264 /* tty untouched since before login */
265 touched
= ep
->utmp
.ut_time
;
267 if ((ep
->idle
= now
- touched
) < 0)
272 if (header
|| wcmd
== 0) {
273 pr_header(&now
, nusers
);
281 #define HEADER_USER "USER"
282 #define HEADER_TTY "TTY"
283 #define HEADER_FROM "FROM"
284 #define HEADER_LOGIN_IDLE "LOGIN@ IDLE "
285 #define HEADER_WHAT "WHAT\n"
286 #define WUSED (UT_NAMESIZE + UT_LINESIZE + W_DISPHOSTSIZE + \
287 sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */
288 (void)printf("%-*.*s %-*.*s %-*.*s %s",
289 UT_NAMESIZE
, UT_NAMESIZE
, HEADER_USER
,
290 UT_LINESIZE
, UT_LINESIZE
, HEADER_TTY
,
291 W_DISPHOSTSIZE
, W_DISPHOSTSIZE
, HEADER_FROM
,
292 HEADER_LOGIN_IDLE HEADER_WHAT
);
296 if ((kp
= kvm_getprocs(kd
, KERN_PROC_ALL
, 0, &nentries
)) == NULL
)
297 err(1, "%s", kvm_geterr(kd
));
301 mib
[2] = KERN_PROC_ALL
;
304 if (sysctl(mib
, 4, NULL
, &bufSize
, NULL
, 0) < 0) {
305 perror("Failure calling sysctl");
309 kprocbuf
= kp
= (struct kinfo_proc
*)malloc(bufSize
);
312 orig_bufSize
= bufSize
;
313 for (retry_count
= 0; ; retry_count
++) {
315 bufSize
= orig_bufSize
;
316 if ((local_error
= sysctl(mib
, 4, kp
, &bufSize
, NULL
, 0)) < 0) {
317 if (retry_count
< 1000) {
321 perror("Failure calling sysctl");
323 } else if (local_error
== 0) {
328 nentries
= bufSize
/ sizeof(struct kinfo_proc
);
330 for (i
= 0; i
< nentries
; i
++, kp
++) {
331 if (kp
->kp_proc
.p_stat
== SIDL
|| kp
->kp_proc
.p_stat
== SZOMB
)
333 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
334 if (ep
->tdev
== kp
->kp_eproc
.e_tdev
) {
336 * proc is associated with this terminal
338 if (ep
->kp
== NULL
&& kp
->kp_eproc
.e_pgid
== kp
->kp_eproc
.e_tpgid
) {
340 * Proc is 'most interesting'
342 if (proc_compare(&ep
->kp
->kp_proc
, &kp
->kp_proc
))
346 * Proc debug option info; add to debug
347 * list using kinfo_proc ki_spare[0]
348 * as next pointer; ptr to ptr avoids the
349 * ptr = long assumption.
359 if ((ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
360 ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
361 ioctl(STDIN_FILENO
, TIOCGWINSZ
, &ws
) == -1) || ws
.ws_col
== 0)
364 ttywidth
= ws
.ws_col
- 1;
365 argwidth
= ttywidth
- WUSED
;
368 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
369 if (ep
->kp
== NULL
) {
370 ep
->args
= strdup("-");
374 ep
->args
= fmt_argv(kvm_getargv(kd
, ep
->kp
, argwidth
),
375 ep
->kp
->kp_proc
.p_comm
, MAXCOMLEN
);
379 if (ep
->args
== NULL
)
382 /* sort by idle time */
383 if (sortidle
&& ehead
!= NULL
) {
384 struct entry
*from
, *save
;
388 while (from
!= NULL
) {
390 (*nextp
) && from
->idle
>= (*nextp
)->idle
;
391 nextp
= &(*nextp
)->next
)
400 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
401 char host_buf
[UT_HOSTSIZE
+ 1];
402 struct sockaddr_storage ss
;
403 struct sockaddr
*sa
= (struct sockaddr
*)&ss
;
404 struct sockaddr_in
*lsin
= (struct sockaddr_in
*)&ss
;
408 struct sockaddr_in6
*lsin6
= (struct sockaddr_in6
*)&ss
;
412 host_buf
[UT_HOSTSIZE
] = '\0';
413 strncpy(host_buf
, ep
->utmp
.ut_host
, UT_HOSTSIZE
);
414 p
= *host_buf
? host_buf
: "-";
415 if ((x_suffix
= strrchr(p
, ':')) != NULL
) {
416 if ((dot
= strchr(x_suffix
, '.')) != NULL
&&
417 strchr(dot
+1, '.') == NULL
)
423 /* Attempt to change an IP address into a name */
425 memset(&ss
, '\0', sizeof(ss
));
427 if (inet_aton(p
, &lsin
->sin_addr
) ) {
428 lsin
->sin_len
= sizeof(*lsin
);
429 lsin
->sin_family
= AF_INET
;
433 hp
= gethostbyaddr((char *)&lsin
->sin_addr
, sizeof(lsin
->sin_addr
), AF_INET
);
438 if (inet_pton(AF_INET6
, p
, &lsin6
->sin6_addr
) == 1) {
439 lsin6
->sin6_len
= sizeof(*lsin6
);
440 lsin6
->sin6_family
= AF_INET6
;
442 } else if (inet_pton(AF_INET
, p
, &lsin
->sin_addr
) == 1) {
443 lsin
->sin_len
= sizeof(*lsin
);
444 lsin
->sin_family
= AF_INET
;
447 if (isaddr
&& realhostname_sa(fn
, sizeof(fn
), sa
,
448 sa
->sa_len
) == HOSTNAME_FOUND
)
453 (void)snprintf(buf
, sizeof(buf
), "%s:%s", p
, x_suffix
);
458 for (dkp
= ep
->dkp
; dkp
!= NULL
; dkp
= debugproc(dkp
)) {
461 ptr
= fmt_argv(kvm_getargv(kd
, dkp
, argwidth
),
462 dkp
->ki_comm
, MAXCOMLEN
);
465 (void)printf("\t\t%-9d %s\n",
470 (void)printf("%-*.*s %-*.*s %-*.*s ",
471 UT_NAMESIZE
, UT_NAMESIZE
, ep
->utmp
.ut_name
,
472 UT_LINESIZE
, UT_LINESIZE
,
473 strncmp(ep
->utmp
.ut_line
, "tty", 3) &&
474 strncmp(ep
->utmp
.ut_line
, "cua", 3) ?
475 ep
->utmp
.ut_line
: ep
->utmp
.ut_line
+ 3,
476 W_DISPHOSTSIZE
, W_DISPHOSTSIZE
, *p
? p
: "-");
477 pr_attime(&ep
->utmp
.ut_time
, &now
);
478 longidle
= pr_idle(ep
->idle
);
479 (void)printf("%.*s\n", argwidth
- longidle
, ep
->args
);
493 pr_header(nowp
, nusers
)
499 int days
, hrs
, i
, mins
, secs
;
507 (void)strftime(buf
, sizeof(buf
) - 1,
508 use_ampm
? "%l:%M%p" : "%k:%M", localtime(nowp
));
509 buf
[sizeof(buf
) - 1] = '\0';
510 (void)printf("%s ", buf
);
513 * Print how long system has been up.
514 * (Found by looking getting "boottime" from the kernel)
517 mib
[1] = KERN_BOOTTIME
;
518 size
= sizeof(boottime
);
519 if (sysctl(mib
, 2, &boottime
, &size
, NULL
, 0) != -1 &&
520 boottime
.tv_sec
!= 0) {
521 uptime
= now
- boottime
.tv_sec
;
524 days
= uptime
/ 86400;
532 (void)printf(" %d day%s,", days
, days
> 1 ? "s" : "");
533 if (hrs
> 0 && mins
> 0)
534 (void)printf(" %2d:%02d,", hrs
, mins
);
536 (void)printf(" %d hr%s,", hrs
, hrs
> 1 ? "s" : "");
538 (void)printf(" %d min%s,", mins
, mins
> 1 ? "s" : "");
540 (void)printf(" %d sec%s,", secs
, secs
> 1 ? "s" : "");
543 /* Print number of users logged in to system */
544 (void)printf(" %d user%s", nusers
, nusers
== 1 ? "" : "s");
547 * Print 1, 5, and 15 minute load averages.
549 if (getloadavg(avenrun
, sizeof(avenrun
) / sizeof(avenrun
[0])) == -1)
550 (void)printf(", no load average information available\n");
552 (void)printf(", load averages:");
553 for (i
= 0; i
< (int)(sizeof(avenrun
) / sizeof(avenrun
[0])); i
++) {
554 if (use_comma
&& i
> 0)
556 (void)printf(" %.2f", avenrun
[i
]);
567 static struct stat sb
;
568 char ttybuf
[MAXPATHLEN
];
570 (void)snprintf(ttybuf
, sizeof(ttybuf
), "%s%.*s", _PATH_DEV
, sz
, line
);
571 if (stat(ttybuf
, &sb
)) {
583 (void)fprintf(stderr
,
584 "usage: w [-dhin] [-M core] [-N system] [user ...]\n");
586 (void)fprintf(stderr
, "usage: uptime\n");
596 if ((u
= strrchr(s
, '/')) != NULL
)
600 if (strcmp(u
, "uptime") == 0)
610 char *procargs
, *sp
, *np
, *cp
;
613 mib
[1] = KERN_ARGMAX
;
615 size
= sizeof(argmax
);
616 if (sysctl(mib
, 2, &argmax
, &size
, NULL
, 0) == -1) {
620 procargs
= malloc(argmax
);
621 if (procargs
== NULL
) {
626 mib
[1] = KERN_PROCARGS
;
627 mib
[2] = KI_PROC(ep
)->p_pid
;
629 size
= (size_t)argmax
;
630 if (sysctl(mib
, 3, procargs
, &size
, NULL
, 0) == -1) {
634 for (cp
= procargs
; cp
< &procargs
[size
]; cp
++) {
639 if (cp
== &procargs
[size
]) {
645 for (np
= NULL
; cp
< &procargs
[size
]; cp
++) {
651 } else if (*cp
== '=') {
656 for (np
= sp
; (np
< &procargs
[size
]) && (*np
== ' '); np
++);
658 ep
->args
= strdup(np
);
666 ep->args = malloc(2);
670 asprintf(&ep
->args
, "%s", KI_PROC(ep
)->p_comm
);