2 * Copyright (c) 1980, 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Portions copyright (c) 2007 Apple Inc. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD: src/usr.bin/w/w.c,v 1.58 2005/06/04 23:40:09 gad Exp $");
38 static const char copyright
[] =
39 "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
40 The Regents of the University of California. All rights reserved.\n";
44 static const char sccsid
[] = "@(#)w.c 8.4 (Berkeley) 4/16/94";
48 * w - print system status (who and what)
50 * This program is similar to the systat command on Tenex/Tops 10/20
53 #include <sys/param.h>
56 #include <sys/sysctl.h>
59 #include <sys/ioctl.h>
60 #include <sys/socket.h>
64 #include <machine/cpu.h>
66 #include <netinet/in.h>
67 #include <arpa/inet.h>
68 #include <arpa/nameser.h>
92 /* use utmp values so formatting is the same */
95 #else /* HAVE_UTMPX */
97 #endif /* HAVE_UTMPX */
100 #include <TargetConditionals.h>
104 struct timeval boottime
;
112 time_t now
; /* the current time of day */
113 int ttywidth
; /* width of tty */
114 int argwidth
; /* width of tty */
115 int header
= 1; /* true if -h flag: don't print heading */
117 int nflag
; /* true if -n flag: don't convert addrs */
120 int dflag
; /* true if -d flag: output debug info */
122 int sortidle
; /* sort by idle time */
123 int use_ampm
; /* use AM/PM time */
124 int use_comma
; /* use comma as floats separator */
125 char **sel_users
; /* login array of particular users selected */
128 * One of these per active utmp entry.
137 dev_t tdev
; /* dev_t of terminal */
138 time_t idle
; /* idle time of terminal in seconds */
139 struct kinfo_proc
*kp
; /* `most interesting' proc */
140 char *args
; /* arg list of interesting process */
141 struct kinfo_proc
*dkp
; /* debug option proc list */
142 } *ep
, *ehead
= NULL
, **nextp
= &ehead
;
145 #define debugproc(p) *((struct kinfo_proc **)&(p)->ki_udata)
147 #define debugproc(p) *((struct kinfo_proc **)&(p)->ki_spare[0])
150 /* W_DISPHOSTSIZE should not be greater than UT_HOSTSIZE */
151 #define W_DISPHOSTSIZE 16
153 static void pr_header(time_t *, int);
154 static struct stat
*ttystat(char *, int);
155 static void usage(int);
156 static int this_is_uptime(const char *s
);
158 static void w_getargv(void);
161 char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */
164 main(int argc
, char *argv
[])
166 struct kinfo_proc
*kp
;
167 struct kinfo_proc
*kprocbuf
;
168 struct kinfo_proc
*dkp
;
177 int ch
, i
, nentries
, nusers
, wcmd
, longidle
, dropgid
;
178 const char *memf
, *nlistf
, *p
;
180 int ch
, i
, nentries
, nusers
, wcmd
, longidle
;
182 #endif /* HAVE_KVM */
185 char buf
[MAXHOSTNAMELEN
];
187 char buf
[MAXHOSTNAMELEN
], errbuf
[_POSIX2_LINE_MAX
];
188 char fn
[MAXHOSTNAMELEN
];
189 #endif /* __APPLE__ */
192 int local_error
= 0, retry_count
= 0;
194 size_t orig_bufSize
= 0;
195 int mib
[4] = {CTL_KERN
, KERN_PROC
, KERN_PROC_ALL
, 0 };
198 (void)setlocale(LC_ALL
, "");
200 use_ampm
= (*nl_langinfo(T_FMT_AMPM
) != '\0');
201 use_comma
= (*nl_langinfo(RADIXCHAR
) != ',');
204 /* Are we w(1) or uptime(1)? */
205 if (this_is_uptime(argv
[0]) == 0) {
215 memf
= nlistf
= _PATH_DEVNULL
;
217 while ((ch
= getopt(argc
, argv
, p
)) != -1)
240 #endif /* HAVE_KVM */
245 #else /* !HAVE_UTMPX */
247 #endif /* !HAVE_UTMPX */
248 case 'f': case 'l': case 's': case 'u': case 'w':
254 warnx("[-MNdflnsuw] no longer supported");
256 warnx("[-flsuw] no longer supported");
266 #if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
267 if (!(_res
.options
& RES_INIT
))
269 _res
.retrans
= 2; /* resolver timeout to 2 seconds per try */
270 _res
.retry
= 1; /* only try once.. */
275 * Discard setgid privileges if not the running kernel so that bad
276 * guys can't print interesting stuff from kernel memory.
281 if ((kd
= kvm_openfiles(nlistf
, memf
, NULL
, O_RDONLY
, errbuf
)) == NULL
)
282 errx(1, "%s", errbuf
);
289 if ((ut
= fopen(_PATH_UTMP
, "r")) == NULL
)
290 err(1, "%s", _PATH_UTMP
);
297 for (nusers
= 0; (ux
= getutxent());) {
298 if (ux
->ut_user
[0] == '\0' || ux
->ut_type
!= USER_PROCESS
)
300 if (!(stp
= ttystat(ux
->ut_line
, sizeof(ux
->ut_line
))))
301 continue; /* corrupted record */
303 for (nusers
= 0; fread(&utmp
, sizeof(utmp
), 1, ut
);) {
304 if (utmp
.ut_name
[0] == '\0')
306 if (!(stp
= ttystat(utmp
.ut_line
, UT_LINESIZE
)))
307 continue; /* corrupted record */
317 for (user
= sel_users
; !usermatch
&& *user
; user
++)
319 if (!strncmp(ux
->ut_user
, *user
, sizeof(ux
->ut_user
)))
321 if (!strncmp(utmp
.ut_name
, *user
, UT_NAMESIZE
))
327 if ((ep
= calloc(1, sizeof(struct entry
))) == NULL
)
332 memmove(&ep
->utmp
, ux
, sizeof(*ux
));
334 memmove(&ep
->utmp
, &utmp
, sizeof(struct utmp
));
336 ep
->tdev
= stp
->st_rdev
;
339 * If this is the console device, attempt to ascertain
340 * the true console device dev_t.
346 mib
[0] = CTL_MACHDEP
;
347 mib
[1] = CPU_CONSDEV
;
348 size
= sizeof(dev_t
);
349 (void)sysctl(mib
, 2, &ep
->tdev
, &size
, NULL
, 0);
352 touched
= stp
->st_atime
;
354 if (touched
< ep
->utmp
.ut_tv
.tv_sec
) {
355 /* tty untouched since before login */
356 touched
= ep
->utmp
.ut_tv
.tv_sec
;
359 if (touched
< ep
->utmp
.ut_time
) {
360 /* tty untouched since before login */
361 touched
= ep
->utmp
.ut_time
;
364 if ((ep
->idle
= now
- touched
) < 0)
373 if (header
|| wcmd
== 0) {
374 pr_header(&now
, nusers
);
382 #define HEADER_USER "USER"
383 #define HEADER_TTY "TTY"
384 #define HEADER_FROM "FROM"
385 #define HEADER_LOGIN_IDLE "LOGIN@ IDLE "
386 #define HEADER_WHAT "WHAT\n"
387 #define WUSED (UT_NAMESIZE + UT_LINESIZE + W_DISPHOSTSIZE + \
388 sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */
389 (void)printf("%-*.*s %-*.*s %-*.*s %s",
390 UT_NAMESIZE
, UT_NAMESIZE
, HEADER_USER
,
391 UT_LINESIZE
, UT_LINESIZE
, HEADER_TTY
,
392 W_DISPHOSTSIZE
, W_DISPHOSTSIZE
, HEADER_FROM
,
393 HEADER_LOGIN_IDLE HEADER_WHAT
);
397 if ((kp
= kvm_getprocs(kd
, KERN_PROC_ALL
, 0, &nentries
)) == NULL
)
398 err(1, "%s", kvm_geterr(kd
));
402 mib
[2] = KERN_PROC_ALL
;
405 if (sysctl(mib
, 4, NULL
, &bufSize
, NULL
, 0) < 0) {
406 perror("Failure calling sysctl");
410 kprocbuf
= kp
= (struct kinfo_proc
*)malloc(bufSize
);
413 orig_bufSize
= bufSize
;
414 for (retry_count
= 0; ; retry_count
++) {
416 bufSize
= orig_bufSize
;
417 if ((local_error
= sysctl(mib
, 4, kp
, &bufSize
, NULL
, 0)) < 0) {
418 if (retry_count
< 1000) {
422 perror("Failure calling sysctl");
424 } else if (local_error
== 0) {
429 nentries
= bufSize
/ sizeof(struct kinfo_proc
);
430 #endif /* !HAVE_KVM */
433 #define ki_stat kp_proc.p_stat
434 #define ki_pgid kp_eproc.e_pgid
435 #define ki_tpgid kp_eproc.e_tpgid
436 #define ki_tdev kp_eproc.e_tdev
437 #endif /* !HAVE_KVM */
438 for (i
= 0; i
< nentries
; i
++, kp
++) {
439 if (kp
->ki_stat
== SIDL
|| kp
->ki_stat
== SZOMB
)
441 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
442 if (ep
->tdev
== kp
->ki_tdev
) {
444 * proc is associated with this terminal
446 if (ep
->kp
== NULL
&& kp
->ki_pgid
== kp
->ki_tpgid
) {
448 * Proc is 'most interesting'
450 if (proc_compare(ep
->kp
, kp
))
454 * Proc debug option info; add to debug
455 * list using kinfo_proc ki_spare[0]
456 * as next pointer; ptr to ptr avoids the
457 * ptr = long assumption.
467 if ((ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
468 ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
469 ioctl(STDIN_FILENO
, TIOCGWINSZ
, &ws
) == -1) || ws
.ws_col
== 0)
472 ttywidth
= ws
.ws_col
- 1;
473 argwidth
= ttywidth
- WUSED
;
476 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
477 if (ep
->kp
== NULL
) {
478 ep
->args
= strdup("-");
482 ep
->args
= fmt_argv(kvm_getargv(kd
, ep
->kp
, argwidth
),
483 ep
->kp
->ki_comm
, MAXCOMLEN
);
486 #endif /* HAVE_KVM */
487 if (ep
->args
== NULL
)
490 /* sort by idle time */
491 if (sortidle
&& ehead
!= NULL
) {
492 struct entry
*from
, *save
;
496 while (from
!= NULL
) {
498 (*nextp
) && from
->idle
>= (*nextp
)->idle
;
499 nextp
= &(*nextp
)->next
)
508 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
510 char host_buf
[sizeof(ep
->utmp
.ut_host
) + 1];
511 strlcpy(host_buf
, ep
->utmp
.ut_host
, sizeof(host_buf
));
513 char host_buf
[UT_HOSTSIZE
+ 1];
514 struct sockaddr_storage ss
;
515 struct sockaddr
*sa
= (struct sockaddr
*)&ss
;
516 struct sockaddr_in
*lsin
= (struct sockaddr_in
*)&ss
;
517 struct sockaddr_in6
*lsin6
= (struct sockaddr_in6
*)&ss
;
521 host_buf
[UT_HOSTSIZE
] = '\0';
522 strncpy(host_buf
, ep
->utmp
.ut_host
, UT_HOSTSIZE
);
523 #endif /* HAVE_UTMPX */
524 p
= *host_buf
? host_buf
: "-";
525 if ((x_suffix
= strrchr(p
, ':')) != NULL
) {
526 if ((dot
= strchr(x_suffix
, '.')) != NULL
&&
527 strchr(dot
+1, '.') == NULL
)
534 /* Attempt to change an IP address into a name */
536 memset(&ss
, '\0', sizeof(ss
));
537 if (inet_pton(AF_INET6
, p
, &lsin6
->sin6_addr
) == 1) {
538 lsin6
->sin6_len
= sizeof(*lsin6
);
539 lsin6
->sin6_family
= AF_INET6
;
541 } else if (inet_pton(AF_INET
, p
, &lsin
->sin_addr
) == 1) {
542 lsin
->sin_len
= sizeof(*lsin
);
543 lsin
->sin_family
= AF_INET
;
546 if (isaddr
&& realhostname_sa(fn
, sizeof(fn
), sa
,
547 sa
->sa_len
) == HOSTNAME_FOUND
)
550 #endif /* !HAVE_UTMPX */
552 (void)snprintf(buf
, sizeof(buf
), "%s:%s", p
, x_suffix
);
557 for (dkp
= ep
->dkp
; dkp
!= NULL
; dkp
= debugproc(dkp
)) {
560 ptr
= fmt_argv(kvm_getargv(kd
, dkp
, argwidth
),
561 dkp
->ki_comm
, MAXCOMLEN
);
564 (void)printf("\t\t%-9d %s\n",
568 #endif /* !__APPLE__ */
569 (void)printf("%-*.*s %-*.*s %-*.*s ",
571 UT_NAMESIZE
, (int)sizeof(ep
->utmp
.ut_user
), ep
->utmp
.ut_user
,
572 UT_LINESIZE
, (int)sizeof(ep
->utmp
.ut_line
),
574 UT_NAMESIZE
, UT_NAMESIZE
, ep
->utmp
.ut_name
,
575 UT_LINESIZE
, UT_LINESIZE
,
577 strncmp(ep
->utmp
.ut_line
, "tty", 3) &&
578 strncmp(ep
->utmp
.ut_line
, "cua", 3) ?
579 ep
->utmp
.ut_line
: ep
->utmp
.ut_line
+ 3,
580 W_DISPHOSTSIZE
, W_DISPHOSTSIZE
, *p
? p
: "-");
582 pr_attime(&ep
->utmp
.ut_tv
.tv_sec
, &now
);
584 t
= _time_to_time32(ep
->utmp
.ut_time
);
587 longidle
= pr_idle(ep
->idle
);
588 (void)printf("%.*s\n", argwidth
- longidle
, ep
->args
);
597 #endif /* HAVE_KVM */
602 pr_header(time_t *nowp
, int nusers
)
606 int days
, hrs
, i
, mins
, secs
;
614 if (strftime(buf
, sizeof(buf
),
615 use_ampm
? "%l:%M%p" : "%k:%M", localtime(nowp
)) != 0)
616 (void)printf("%s ", buf
);
618 * Print how long system has been up.
619 * (Found by looking getting "boottime" from the kernel)
622 mib
[1] = KERN_BOOTTIME
;
623 size
= sizeof(boottime
);
624 if (sysctl(mib
, 2, &boottime
, &size
, NULL
, 0) != -1 &&
625 boottime
.tv_sec
!= 0) {
626 uptime
= now
- boottime
.tv_sec
;
629 days
= uptime
/ 86400;
637 (void)printf(" %d day%s,", days
, days
> 1 ? "s" : "");
638 if (hrs
> 0 && mins
> 0)
639 (void)printf(" %2d:%02d,", hrs
, mins
);
641 (void)printf(" %d hr%s,", hrs
, hrs
> 1 ? "s" : "");
643 (void)printf(" %d min%s,", mins
, mins
> 1 ? "s" : "");
645 (void)printf(" %d sec%s,", secs
, secs
> 1 ? "s" : "");
648 /* Print number of users logged in to system */
649 (void)printf(" %d user%s", nusers
, nusers
== 1 ? "" : "s");
652 * Print 1, 5, and 15 minute load averages.
654 if (getloadavg(avenrun
, sizeof(avenrun
) / sizeof(avenrun
[0])) == -1)
655 (void)printf(", no load average information available\n");
657 (void)printf(", load averages:");
658 for (i
= 0; i
< (int)(sizeof(avenrun
) / sizeof(avenrun
[0])); i
++) {
659 if (use_comma
&& i
> 0)
661 (void)printf(" %.2f", avenrun
[i
]);
668 ttystat(char *line
, int sz
)
670 static struct stat sb
;
671 char ttybuf
[MAXPATHLEN
];
673 (void)snprintf(ttybuf
, sizeof(ttybuf
), "%s%.*s", _PATH_DEV
, sz
, line
);
674 if (stat(ttybuf
, &sb
) == 0) {
686 (void)fprintf(stderr
,
687 "usage: w [hi] [user ...]\n");
689 (void)fprintf(stderr
, "usage: uptime\n");
694 this_is_uptime(const char *s
)
698 if ((u
= strrchr(s
, '/')) != NULL
)
702 if (strcmp(u
, "uptime") == 0)
713 char *procargs
, *sp
, *np
, *cp
;
716 mib
[1] = KERN_ARGMAX
;
718 size
= sizeof(argmax
);
719 if (sysctl(mib
, 2, &argmax
, &size
, NULL
, 0) == -1) {
723 procargs
= malloc(argmax
);
724 if (procargs
== NULL
) {
729 mib
[1] = KERN_PROCARGS
;
730 mib
[2] = KI_PROC(ep
)->p_pid
;
732 size
= (size_t)argmax
;
733 if (sysctl(mib
, 3, procargs
, &size
, NULL
, 0) == -1) {
737 for (cp
= procargs
; cp
< &procargs
[size
]; cp
++) {
742 if (cp
== &procargs
[size
]) {
748 for (np
= NULL
; cp
< &procargs
[size
]; cp
++) {
754 } else if (*cp
== '=') {
759 for (np
= sp
; (np
< &procargs
[size
]) && (*np
== ' '); np
++);
761 ep
->args
= strdup(np
);
769 ep->args = malloc(2);
773 asprintf(&ep
->args
, "%s", KI_PROC(ep
)->p_comm
);
776 #endif /* HAVE_KVM */