1 /* $NetBSD: w.c,v 1.77 2013/09/09 19:20:38 christos Exp $ */
4 * Copyright (c) 1980, 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993, 1994\
35 The Regents of the University of California. All rights reserved.");
40 static char sccsid
[] = "@(#)w.c 8.6 (Berkeley) 6/30/94";
42 __RCSID("$NetBSD: w.c,v 1.77 2013/09/09 19:20:38 christos Exp $");
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>
53 #include <sys/types.h>
56 #include <sys/sysctl.h>
59 #include <sys/ioctl.h>
60 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
91 /* MINIX3 note: please see this header for information about the port. */
92 #include "minix_proc.h"
95 struct timeval boottime
;
98 time_t now
; /* the current time of day */
99 int ttywidth
; /* width of tty */
100 int argwidth
; /* width of tty left to print process args */
101 int header
= 1; /* true if -h flag: don't print heading */
102 int nflag
; /* true if -n flag: don't convert addrs */
103 int wflag
; /* true if -w flag: wide printout */
104 int sortidle
; /* sort bu idle time */
105 char *sel_user
; /* login of particular user selected */
106 char domain
[MAXHOSTNAMELEN
+ 1];
107 int maxname
= 8, maxline
= 3, maxhost
= 16;
110 * One of these per active utmp entry.
114 char name
[UTX_USERSIZE
+ 1];
115 char line
[UTX_LINESIZE
+ 1];
116 char host
[UTX_HOSTSIZE
+ 1];
119 dev_t tdev
; /* dev_t of terminal */
120 time_t idle
; /* idle time of terminal in seconds */
121 struct kinfo_proc2
*tp
; /* `most interesting' tty proc */
122 struct kinfo_proc2
*pp
; /* pid proc */
123 pid_t pid
; /* pid or ~0 if not known */
124 } *ehead
= NULL
, **nextp
= &ehead
;
126 static void pr_args(struct kinfo_proc2
*);
127 static void pr_header(time_t *, int);
129 static int proc_compare_wrapper(const struct kinfo_proc2
*,
130 const struct kinfo_proc2
*);
131 #endif /* !__minix */
132 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
133 static int ttystat(const char *, struct stat
*);
134 static void process(struct entry
*);
136 static void fixhost(struct entry
*ep
);
137 __dead
static void usage(int);
140 main(int argc
, char **argv
)
142 struct kinfo_proc2
*kp
;
144 int ch
, i
, nentries
, nusers
, wcmd
, curtain
, use_sysctl
;
145 char *memf
, *nlistf
, *usrnp
;
150 #endif /* !__minix */
157 const char *progname
;
159 char errbuf
[_POSIX2_LINE_MAX
];
160 #endif /* !__minix */
162 setprogname(argv
[0]);
164 /* Are we w(1) or uptime(1)? */
165 progname
= getprogname();
166 if (*progname
== '-')
168 if (*progname
== 'u') {
173 options
= "hiM:N:nw";
176 memf
= nlistf
= NULL
;
177 while ((ch
= getopt(argc
, argv
, options
)) != -1)
205 use_sysctl
= (memf
== NULL
&& nlistf
== NULL
);
208 if ((kd
= kvm_openfiles(nlistf
, memf
, NULL
,
209 memf
== NULL
? KVM_NO_FILES
: O_RDONLY
, errbuf
)) == NULL
)
210 errx(1, "%s", errbuf
);
213 errx(1, "The -M and -N flags are not supported on MINIX3.");
221 len
= sizeof(curtain
);
222 if (sysctlbyname("security.curtain", &curtain
, &len
,
224 #endif /* !__minix */
240 while ((utx
= getutxent()) != NULL
) {
241 if (utx
->ut_type
!= USER_PROCESS
)
245 strncmp(utx
->ut_name
, sel_user
, sizeof(utx
->ut_name
)) != 0)
247 if ((ep
= calloc(1, sizeof(struct entry
))) == NULL
)
249 (void)memcpy(ep
->name
, utx
->ut_name
, sizeof(utx
->ut_name
));
250 (void)memcpy(ep
->line
, utx
->ut_line
, sizeof(utx
->ut_line
));
251 ep
->name
[sizeof(utx
->ut_name
)] = '\0';
252 ep
->line
[sizeof(utx
->ut_line
)] = '\0';
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
;
272 while ((ut
= getutent()) != NULL
) {
273 if (ut
->ut_name
[0] == '\0')
277 strncmp(ut
->ut_name
, sel_user
, sizeof(ut
->ut_name
)) != 0)
280 /* Don't process entries that we have utmpx for */
281 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
282 if (strncmp(ep
->line
, ut
->ut_line
,
283 sizeof(ut
->ut_line
)) == 0)
290 if ((ep
= calloc(1, sizeof(struct entry
))) == NULL
)
292 (void)memcpy(ep
->name
, ut
->ut_name
, sizeof(ut
->ut_name
));
293 (void)memcpy(ep
->line
, ut
->ut_line
, sizeof(ut
->ut_line
));
294 (void)memcpy(ep
->host
, ut
->ut_host
, sizeof(ut
->ut_host
));
295 ep
->name
[sizeof(ut
->ut_name
)] = '\0';
296 ep
->line
[sizeof(ut
->ut_line
)] = '\0';
297 ep
->host
[sizeof(ut
->ut_host
)] = '\0';
299 ep
->tv
.tv_sec
= ut
->ut_time
;
314 if (header
|| wcmd
== 0) {
315 pr_header(&now
, nusers
);
320 if ((kp
= kvm_getproc2(kd
, KERN_PROC_ALL
, 0,
321 sizeof(struct kinfo_proc2
), &nentries
)) == NULL
)
322 errx(1, "%s", kvm_geterr(kd
));
324 /* Include trailing space because TTY header starts one column early. */
325 for (i
= 0; i
< nentries
; i
++, kp
++) {
327 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
328 if (ep
->tdev
!= 0 && ep
->tdev
== kp
->p_tdev
&&
329 kp
->p__pgid
== kp
->p_tpgid
) {
331 * Proc is in foreground of this
334 if (proc_compare_wrapper(ep
->tp
, kp
))
338 if (ep
->pid
!= 0 && ep
->pid
== kp
->p_pid
) {
345 if ((ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
346 ioctl(STDERR_FILENO
, TIOCGWINSZ
, &ws
) == -1 &&
347 ioctl(STDIN_FILENO
, TIOCGWINSZ
, &ws
) == -1) || ws
.ws_col
== 0)
350 ttywidth
= ws
.ws_col
- 1;
352 if (!wflag
&& maxhost
> (ttywidth
/ 3))
353 maxhost
= ttywidth
/ 3;
355 argwidth
= printf("%-*s TTY %-*s %*s IDLE WHAT\n",
356 maxname
, "USER", maxhost
, "FROM",
357 7 /* "dddhhXm" */, "LOGIN@");
358 argwidth
-= sizeof("WHAT\n") - 1 /* NUL */;
359 argwidth
= ttywidth
- argwidth
;
365 /* sort by idle time */
366 if (sortidle
&& ehead
!= NULL
) {
367 struct entry
*from
= ehead
, *save
;
370 while (from
!= NULL
) {
372 (*nextp
) && from
->idle
>= (*nextp
)->idle
;
373 nextp
= &(*nextp
)->next
)
381 #if defined(SUPPORT_UTMP) && defined(SUPPORT_UTMPX)
382 else if (ehead
!= NULL
) {
383 struct entry
*from
= ehead
, *save
;
386 while (from
!= NULL
) {
388 (*nextp
) && strcmp(from
->line
, (*nextp
)->line
) > 0;
389 nextp
= &(*nextp
)->next
)
403 rv
= gethostname(domain
, sizeof(domain
));
404 domain
[sizeof(domain
) - 1] = '\0';
405 if (rv
< 0 || (p
= strchr(domain
, '.')) == 0)
408 memmove(domain
, p
, strlen(p
) + 1);
411 for (ep
= ehead
; ep
!= NULL
; ep
= ep
->next
) {
414 else if (ep
->pp
!= NULL
)
416 else if (ep
->pid
!= 0) {
420 warnx("Stale utmp%s entry: %s %s %s",
421 ep
->type
, ep
->name
, ep
->line
, ep
->host
);
426 usrnp
= (kp
== NULL
) ? ep
->name
: kp
->p_login
;
428 usrnp
= ep
->name
; /* TODO: implement getlogin/setlogin */
430 (void)printf("%-*s %-7.7s %-*.*s ",
431 maxname
, usrnp
, ep
->line
,
432 maxhost
, maxhost
, ep
->host
);
433 then
= (time_t)ep
->tv
.tv_sec
;
434 pr_attime(&then
, &now
);
443 pr_args(struct kinfo_proc2
*kp
)
451 argv
= kvm_getargv2(kd
, kp
, (argwidth
< 0) ? 0 : argwidth
);
453 if (kp
->p_comm
== 0) {
456 fmt_putc('(', &left
);
457 fmt_puts((char *)kp
->p_comm
, &left
);
458 fmt_putc(')', &left
);
463 fmt_puts(*argv
, &left
);
465 fmt_putc(' ', &left
);
473 pr_header(time_t *nowp
, int nusers
)
489 * SCCS forces the string manipulation below, as it replaces
490 * %, M, and % in a character string with the file name.
492 (void)strftime(buf
, sizeof(buf
), "%l:%" "M%p", localtime(nowp
));
493 buf
[sizeof(buf
) - 1] = '\0';
494 (void)printf("%s ", buf
);
497 * Print how long system has been up.
498 * (Found by looking getting "boottime" from the kernel)
502 mib
[1] = KERN_BOOTTIME
;
503 size
= sizeof(boottime
);
504 if (sysctl(mib
, 2, &boottime
, &size
, NULL
, 0) != -1 &&
505 boottime
.tv_sec
!= 0) {
506 uptime
= now
- boottime
.tv_sec
;
508 if (minix_getuptime(&uptime
) != -1) {
511 if (uptime
> SECSPERMIN
) {
512 days
= uptime
/ SECSPERDAY
;
513 uptime
%= SECSPERDAY
;
514 hrs
= uptime
/ SECSPERHOUR
;
515 uptime
%= SECSPERHOUR
;
516 mins
= uptime
/ SECSPERMIN
;
519 (void)printf(" %d day%s,", days
,
520 days
> 1 ? "s" : "");
521 if (hrs
> 0 && mins
> 0)
522 (void)printf(" %2d:%02d,", hrs
, mins
);
525 (void)printf(" %d hr%s,",
526 hrs
, hrs
> 1 ? "s" : "");
528 (void)printf(" %d min%s,",
529 mins
, mins
> 1 ? "s" : "");
534 /* Print number of users logged in to system */
535 (void)printf(" %d user%s", nusers
, nusers
!= 1 ? "s" : "");
538 * Print 1, 5, and 15 minute load averages.
540 if (getloadavg(avenrun
, sizeof(avenrun
) / sizeof(avenrun
[0])) == -1)
541 (void)printf(", no load average information available\n");
543 (void)printf(", load averages:");
544 for (i
= 0; i
< (sizeof(avenrun
) / sizeof(avenrun
[0])); i
++) {
547 (void)printf(" %.2f", avenrun
[i
]);
553 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
555 ttystat(const char *line
, struct stat
*st
)
557 char ttybuf
[MAXPATHLEN
];
559 (void)snprintf(ttybuf
, sizeof(ttybuf
), "%s%s", _PATH_DEV
, line
);
560 return stat(ttybuf
, st
);
564 process(struct entry
*ep
)
570 if ((max
= strlen(ep
->name
)) > maxname
)
572 if ((max
= strlen(ep
->line
)) > maxline
)
574 if ((max
= strlen(ep
->host
)) > maxhost
)
578 ep
->idle
= (time_t)-1;
582 * Hack to recognize and correctly parse
583 * ut entry made by ftpd. The "tty" used
584 * by ftpd is not a real tty, just identifier in
585 * form ftpPID. Pid parsed from the "tty name"
586 * is used later to match corresponding process.
587 * NB: This is only used for utmp entries. For utmpx,
588 * we already have the pid.
590 if (ep
->pid
== 0 && strncmp(ep
->line
, "ftp", 3) == 0) {
591 ep
->pid
= strtol(ep
->line
+ 3, NULL
, 10);
595 if (ttystat(ep
->line
, &st
) == -1)
598 ep
->tdev
= st
.st_rdev
;
600 * If this is the console device, attempt to ascertain
601 * the true console device dev_t.
608 mib
[1] = KERN_CONSDEV
;
609 size
= sizeof(dev_t
);
610 (void) sysctl(mib
, 2, &ep
->tdev
, &size
, NULL
, 0);
613 touched
= st
.st_atime
;
614 if (touched
< ep
->tv
.tv_sec
) {
615 /* tty untouched since before login */
616 touched
= ep
->tv
.tv_sec
;
618 if ((ep
->idle
= now
- touched
) < 0)
625 proc_compare_wrapper(const struct kinfo_proc2
*p1
,
626 const struct kinfo_proc2
*p2
)
628 struct kinfo_lwp
*l1
, *l2
;
634 l1
= kvm_getlwps(kd
, p1
->p_pid
, 0, sizeof(*l1
), &cnt
);
635 if (l1
== NULL
|| cnt
== 0)
638 l2
= kvm_getlwps(kd
, p2
->p_pid
, 0, sizeof(*l1
), &cnt
);
639 if (l2
== NULL
|| cnt
== 0)
642 return proc_compare(p1
, l1
, p2
, l2
);
644 #endif /* !__minix */
647 fixhost(struct entry
*ep
)
649 char host_buf
[sizeof(ep
->host
)];
654 strlcpy(host_buf
, *ep
->host
? ep
->host
: "-", sizeof(host_buf
));
658 * XXX: Historical behavior, ':' in hostname means X display number,
661 for (x
= p
; x
< &host_buf
[sizeof(host_buf
)]; x
++)
662 if (*x
== '\0' || *x
== ':')
664 if (x
== p
+ sizeof(host_buf
) || *x
!= ':')
669 if (!nflag
&& inet_aton(p
, &l
) &&
670 (hp
= gethostbyaddr((char *)&l
, sizeof(l
), AF_INET
))) {
671 if (domain
[0] != '\0') {
673 p
+= strlen(hp
->h_name
);
675 if (p
> hp
->h_name
&&
676 strcasecmp(p
, domain
) == 0)
683 (void)snprintf(ep
->host
, sizeof(ep
->host
), "%s:%s", p
, x
);
686 strlcpy(ep
->host
, p
, sizeof(ep
->host
));
694 (void)fprintf(stderr
,
695 "Usage: %s [-hinw] [-M core] [-N system] [user]\n",
698 (void)fprintf(stderr
, "Usage: %s\n", getprogname());