2 * Copyright (c) 1983, 1993
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) 1983, 1993 The Regents of the University of California. All rights reserved.
34 * @(#)rwhod.c 8.1 (Berkeley) 6/6/93
35 * $FreeBSD: src/usr.sbin/rwhod/rwhod.c,v 1.13.2.2 2000/12/23 15:28:12 iedowse Exp $
36 * $DragonFly: src/usr.sbin/rwhod/rwhod.c,v 1.24 2007/12/27 15:29:40 matthias Exp $
39 #include <sys/param.h>
40 #include <sys/socket.h>
42 #include <sys/signal.h>
43 #include <sys/ioctl.h>
44 #include <sys/sysctl.h>
47 #include <net/if_dl.h>
48 #include <net/route.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 #include <protocols/rwhod.h>
71 * This version of Berkeley's rwhod has been modified to use IP multicast
72 * datagrams, under control of a new command-line option:
74 * rwhod -m causes rwhod to use IP multicast (instead of
75 * broadcast or unicast) on all interfaces that have
76 * the IFF_MULTICAST flag set in their "ifnet" structs
77 * (excluding the loopback interface). The multicast
78 * reports are sent with a time-to-live of 1, to prevent
79 * forwarding beyond the directly-connected subnet(s).
81 * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a
82 * time-to-live of <ttl>, via a SINGLE interface rather
83 * than all interfaces. <ttl> must be between 0 and
84 * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1"
85 * is different than "-m", in that "-m 1" specifies
86 * transmission on one interface only.
88 * When "-m" is used without a <ttl> argument, the program accepts multicast
89 * rwhod reports from all multicast-capable interfaces. If a <ttl> argument
90 * is given, it accepts multicast reports from only one interface, the one
91 * on which reports are sent (which may be controlled via the host's routing
92 * table). Regardless of the "-m" option, the program accepts broadcast or
93 * unicast reports from all interfaces. Thus, this program will hear the
94 * reports of old, non-multicasting rwhods, but, if multicasting is used,
95 * those old rwhods won't hear the reports generated by this program.
97 * -- Steve Deering, Stanford University, February 1989
100 #define UNPRIV_USER "daemon"
101 #define UNPRIV_GROUP "daemon"
103 #define WITH_ERRNO 1 /* Write to syslog with errno (%m) */
104 #define WITHOUT_ERRNO 0 /* Write to syslog without errno */
106 #define NO_MULTICAST 0 /* multicast modes */
107 #define PER_INTERFACE_MULTICAST 1
108 #define SCOPED_MULTICAST 2
110 #define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */
112 #define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */
113 /* (belongs in protocols/rwhod.h) */
117 int iff_flag
= IFF_POINTOPOINT
;
118 int multicast_mode
= NO_MULTICAST
;
120 struct sockaddr_in multicast_addr
;
122 char myname
[MAXHOSTNAMELEN
];
125 * We communicate with each neighbor in a list constructed at the time we're
126 * started up. Neighbors are currently directly connected via a hardware
130 struct neighbor
*n_next
;
131 char *n_name
; /* interface name */
132 struct sockaddr
*n_addr
; /* who to send to */
133 int n_addrlen
; /* size of address */
134 int n_flags
; /* should forward?, interface flags */
137 struct neighbor
*neighbors
;
141 volatile sig_atomic_t onsighup
;
143 #define WHDRSIZE (sizeof(mywd) - sizeof(mywd.wd_we))
145 void run_as(uid_t
*, gid_t
*);
147 void getboottime(void);
148 void send_host_information(void);
150 void quit(const char *, int);
151 void rt_xaddrs(caddr_t
, caddr_t
, struct rt_addrinfo
*);
152 int verify(char *, int);
153 void handleread(int);
154 static void usage(void);
155 void timeadd(struct timeval
*, time_t, struct timeval
*);
158 char *interval(int, const char *);
159 ssize_t
Sendto(int, const void *, size_t, int,
160 const struct sockaddr
*, int);
162 #define Sendto sendto
166 main(int argc
, char *argv
[])
168 struct pollfd pfd
[1];
171 int send_time
= 180; /* Default time, 180 seconds (3 minutes) */
172 struct sockaddr_in m_sin
;
177 struct timeval next
, now
;
180 errx(1, "not super user");
182 run_as(&unpriv_uid
, &unpriv_gid
);
185 while (argc
> 0 && *argv
[0] == '-') {
186 if (strcmp(*argv
, "-m") == 0) {
187 if (argc
> 1 && *(argv
+ 1)[0] != '-') {
188 /* Argument has been given */
190 multicast_mode
= SCOPED_MULTICAST
;
191 tmp
= strtol(*argv
, &ep
, 10);
192 if (*ep
!= '\0' || tmp
< INT_MIN
|| tmp
> INT_MAX
)
193 errx(1, "invalid ttl: %s", *argv
);
194 multicast_scope
= (int)tmp
;
195 if (multicast_scope
> MAX_MULTICAST_SCOPE
)
196 errx(1, "ttl must not exceed %u",
197 MAX_MULTICAST_SCOPE
);
199 else multicast_mode
= PER_INTERFACE_MULTICAST
;
200 } else if (strcmp(*argv
, "-g") == 0) {
201 if (argc
> 1 && *(argv
+ 1)[0] != '-') {
203 send_time
= (int)strtol(*argv
, &ep
, 10);
205 errx(1, "time must be greater than 0");
209 errx(1, "invalid argument: %s", *argv
);
210 if (*ep
== 'M' || *ep
== 'm') {
211 /* Time in minutes. */
214 errx(1, "invalid argument: %s", *argv
);
218 errx(1, "cannot be greater than 180 seconds (3 minutes)");
220 errx(1, "missing argument");
221 } else if (strcmp(*argv
, "-i") == 0)
223 else if (strcmp(*argv
, "-l") == 0)
225 else if (strcmp(*argv
, "-p") == 0)
238 openlog("rwhod", LOG_PID
, LOG_DAEMON
);
239 sp
= getservbyname("who", "udp");
241 quit("udp/who: unknown service", WITHOUT_ERRNO
);
243 if (chdir(_PATH_RWHODIR
) < 0)
244 quit(_PATH_RWHODIR
, WITH_ERRNO
);
247 * Establish host name as returned by system.
249 if (gethostname(myname
, sizeof(myname
)) < 0)
250 quit("gethostname", WITH_ERRNO
);
252 if ((cp
= strchr(myname
, '.')) != NULL
)
254 strlcpy(mywd
.wd_hostname
, myname
, sizeof(mywd
.wd_hostname
));
255 utmpf
= open(_PATH_UTMP
, O_RDONLY
|O_CREAT
, 0644);
257 quit(_PATH_UTMP
, WITH_ERRNO
);
260 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
261 quit("socket", WITH_ERRNO
);
263 if (setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &on
, sizeof(on
)) < 0)
264 quit("setsockopt SO_BROADCAST", WITH_ERRNO
);
266 memset(&m_sin
, 0, sizeof(m_sin
));
267 m_sin
.sin_len
= sizeof(m_sin
);
268 m_sin
.sin_family
= AF_INET
;
269 m_sin
.sin_port
= sp
->s_port
;
270 if (bind(s
, (struct sockaddr
*)&m_sin
, sizeof(m_sin
)) < 0)
271 quit("bind", WITH_ERRNO
);
274 setgroups(1, &unpriv_gid
); /* XXX BOGUS groups[0] = egid */
280 send_host_information();
282 gettimeofday(&now
, NULL
);
283 timeadd(&now
, delta
, &next
);
287 pfd
[0].events
= POLLIN
;
292 n
= poll(pfd
, 1, 1000);
302 gettimeofday(&now
, NULL
);
303 if (now
.tv_sec
> next
.tv_sec
) {
304 send_host_information();
305 timeadd(&now
, delta
, &next
);
312 timeadd(struct timeval
*now
, time_t delta
, struct timeval
*next
)
314 (next
)->tv_sec
= (now
)->tv_sec
+ delta
;
320 struct sockaddr_in from
;
325 socklen_t len
= sizeof(from
);
327 cc
= recvfrom(sock
, (char *)&wd
, sizeof(struct whod
), 0,
328 (struct sockaddr
*)&from
, &len
);
332 if (cc
< 0 && errno
!= EINTR
) {
333 syslog(LOG_WARNING
, "recv: %m");
337 if (from
.sin_port
!= sp
->s_port
&& !insecure_mode
) {
338 syslog(LOG_WARNING
, "%d: bad source port from %s",
339 ntohs(from
.sin_port
), inet_ntoa(from
.sin_addr
));
342 if (cc
< (int)WHDRSIZE
) {
343 syslog(LOG_WARNING
, "short packet from %s",
344 inet_ntoa(from
.sin_addr
));
347 if (wd
.wd_vers
!= WHODVERSION
)
349 if (wd
.wd_type
!= WHODTYPE_STATUS
)
351 if (!verify(wd
.wd_hostname
, sizeof wd
.wd_hostname
)) {
352 syslog(LOG_WARNING
, "malformed host name from %s",
353 inet_ntoa(from
.sin_addr
));
356 snprintf(path
, sizeof path
, "whod.%s", wd
.wd_hostname
);
358 * Rather than truncating and growing the file each time,
359 * use ftruncate if size is less than previous size.
361 whod
= open(path
, O_WRONLY
| O_CREAT
, 0644);
363 syslog(LOG_WARNING
, "%s: %m", path
);
366 #if ENDIAN != BIG_ENDIAN
368 int i
, n
= (cc
- WHDRSIZE
)/sizeof(struct whoent
);
371 /* undo header byte swapping before writing to file */
372 wd
.wd_sendtime
= ntohl(wd
.wd_sendtime
);
373 for (i
= 0; i
< 3; i
++)
374 wd
.wd_loadav
[i
] = ntohl(wd
.wd_loadav
[i
]);
375 wd
.wd_boottime
= ntohl(wd
.wd_boottime
);
377 for (i
= 0; i
< n
; i
++) {
378 we
->we_idle
= ntohl(we
->we_idle
);
379 we
->we_utmp
.out_time
=
380 ntohl(we
->we_utmp
.out_time
);
385 time((time_t *)&wd
.wd_recvtime
);
386 write(whod
, (char *)&wd
, cc
);
387 if (fstat(whod
, &st
) < 0 || st
.st_size
> cc
)
395 fprintf(stderr
, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n");
400 hup(int signo __unused
)
406 run_as(uid_t
*uid
, gid_t
*gid
)
411 pw
= getpwnam(UNPRIV_USER
);
413 quit("getpwnam(daemon)", WITH_ERRNO
);
417 gr
= getgrnam(UNPRIV_GROUP
);
419 quit("getgrnam(daemon)", WITH_ERRNO
);
425 * Check out host name for unprintables
426 * and other funnies before allowing a file
427 * to be created. Sorry, but blanks aren't allowed.
430 verify(char *name
, int maxlen
)
434 while (*name
&& size
< maxlen
- 1) {
435 if (!isascii(*name
) || !(isalnum(*name
) || ispunct(*name
)))
450 send_host_information(void)
453 struct whoent
*we
= mywd
.wd_we
, *wlast
;
461 if (alarmcount
% 10 == 0)
465 if ((stb
.st_mtime
!= utmptime
) || (stb
.st_size
> utmpsize
)) {
466 utmptime
= stb
.st_mtime
;
467 if (stb
.st_size
> utmpsize
) {
468 utmpsize
= stb
.st_size
+ 10 * sizeof(struct utmp
);
469 utmp
= reallocf(utmp
, utmpsize
);
471 syslog(LOG_WARNING
, "malloc failed: %m");
476 lseek(utmpf
, (off_t
)0, L_SET
);
477 cc
= read(utmpf
, (char *)utmp
, stb
.st_size
);
479 syslog(LOG_ERR
, "read(%s): %m", _PATH_UTMP
);
482 wlast
= &mywd
.wd_we
[1024 / sizeof(struct whoent
) - 1];
483 utmpent
= cc
/ sizeof(struct utmp
);
484 for (i
= 0; i
< utmpent
; i
++)
485 if (utmp
[i
].ut_name
[0]) {
486 memcpy(we
->we_utmp
.out_line
, utmp
[i
].ut_line
,
487 sizeof(utmp
[i
].ut_line
));
488 memcpy(we
->we_utmp
.out_name
, utmp
[i
].ut_name
,
489 sizeof(utmp
[i
].ut_name
));
490 we
->we_utmp
.out_time
= htonl(utmp
[i
].ut_time
);
495 utmpent
= we
- mywd
.wd_we
;
499 * The test on utmpent looks silly---after all, if no one is
500 * logged on, why worry about efficiency?---but is useful on
501 * (e.g.) compute servers.
503 if (utmpent
&& chdir(_PATH_DEV
)) {
504 syslog(LOG_ERR
, "chdir(%s): %m", _PATH_DEV
);
509 for (i
= 0; i
< utmpent
; i
++) {
510 if (stat(we
->we_utmp
.out_line
, &stb
) >= 0)
511 we
->we_idle
= htonl(now
- stb
.st_atime
);
514 getloadavg(avenrun
, sizeof(avenrun
)/sizeof(avenrun
[0]));
515 for (i
= 0; i
< 3; i
++)
516 mywd
.wd_loadav
[i
] = htonl((u_long
)(avenrun
[i
] * 100));
517 cc
= (char *)we
- (char *)&mywd
;
518 mywd
.wd_sendtime
= htonl(time(0));
519 mywd
.wd_vers
= WHODVERSION
;
520 mywd
.wd_type
= WHODTYPE_STATUS
;
521 if (multicast_mode
== SCOPED_MULTICAST
) {
522 Sendto(s
, (char *)&mywd
, cc
, 0,
523 (struct sockaddr
*)&multicast_addr
,
524 sizeof(multicast_addr
));
526 else for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
) {
527 if (multicast_mode
== PER_INTERFACE_MULTICAST
&&
528 np
->n_flags
& IFF_MULTICAST
) {
530 * Select the outgoing interface for the multicast.
532 if (setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
,
533 &(((struct sockaddr_in
*)np
->n_addr
)->sin_addr
),
534 sizeof(struct in_addr
)) < 0) {
535 quit("setsockopt IP_MULTICAST_IF", WITH_ERRNO
);
537 Sendto(s
, (char *)&mywd
, cc
, 0,
538 (struct sockaddr
*)&multicast_addr
,
539 sizeof(multicast_addr
));
540 } else Sendto(s
, (char *)&mywd
, cc
, 0,
541 np
->n_addr
, np
->n_addrlen
);
543 if (utmpent
&& chdir(_PATH_RWHODIR
)) {
544 syslog(LOG_ERR
, "chdir(%s): %m", _PATH_RWHODIR
);
557 mib
[1] = KERN_BOOTTIME
;
559 if (sysctl(mib
, 2, &tm
, &size
, NULL
, 0) == -1)
560 quit("cannot get boottime", WITH_ERRNO
);
562 mywd
.wd_boottime
= htonl(tm
.tv_sec
);
566 * If wrterrno == WITH_ERRNO, we will print
567 * errno. If not, we leave errno out.
570 quit(const char *msg
, int wrterrno
)
573 syslog(LOG_ERR
, "%s: %m", msg
);
575 syslog(LOG_ERR
, "%s", msg
);
580 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
581 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
584 rt_xaddrs(caddr_t cp
, caddr_t cplim
, struct rt_addrinfo
*rtinfo
)
589 memset(rtinfo
->rti_info
, 0, sizeof(rtinfo
->rti_info
));
590 for (i
= 0; (i
< RTAX_MAX
) && (cp
< cplim
); i
++) {
591 if ((rtinfo
->rti_addrs
& (1 << i
)) == 0)
593 rtinfo
->rti_info
[i
] = sa
= (struct sockaddr
*)cp
;
599 * Figure out device configuration and select
600 * networks which deserve status information.
603 configure(int c_sock
)
606 struct if_msghdr
*ifm
;
607 struct ifa_msghdr
*ifam
;
608 struct sockaddr_dl
*sdl
;
610 int mib
[6], flags
= 0, len
;
611 char *buf
, *lim
, *next
;
612 struct rt_addrinfo info
;
614 if (multicast_mode
!= NO_MULTICAST
) {
615 multicast_addr
.sin_len
= sizeof(multicast_addr
);
616 multicast_addr
.sin_family
= AF_INET
;
617 multicast_addr
.sin_addr
.s_addr
= htonl(INADDR_WHOD_GROUP
);
618 multicast_addr
.sin_port
= sp
->s_port
;
621 if (multicast_mode
== SCOPED_MULTICAST
) {
625 mreq
.imr_multiaddr
.s_addr
= htonl(INADDR_WHOD_GROUP
);
626 mreq
.imr_interface
.s_addr
= htonl(INADDR_ANY
);
627 if (setsockopt(c_sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
628 &mreq
, sizeof(mreq
)) < 0) {
630 "setsockopt IP_ADD_MEMBERSHIP: %m");
633 ttl
= multicast_scope
;
634 if (setsockopt(c_sock
, IPPROTO_IP
, IP_MULTICAST_TTL
,
635 &ttl
, sizeof(ttl
)) < 0) {
637 "setsockopt IP_MULTICAST_TTL: %m");
647 mib
[4] = NET_RT_IFLIST
;
649 if (sysctl(mib
, 6, NULL
, &needed
, NULL
, 0) < 0)
650 quit("route-sysctl-estimate", WITH_ERRNO
);
651 if ((buf
= malloc(needed
)) == NULL
)
652 quit("malloc", WITH_ERRNO
);
653 if (sysctl(mib
, 6, buf
, &needed
, NULL
, 0) < 0)
654 quit("actual retrieval of interface table", WITH_ERRNO
);
657 sdl
= NULL
; /* XXX just to keep gcc -Wall happy */
658 for (next
= buf
; next
< lim
; next
+= ifm
->ifm_msglen
) {
659 ifm
= (struct if_msghdr
*)next
;
660 if (ifm
->ifm_type
== RTM_IFINFO
) {
661 sdl
= (struct sockaddr_dl
*)(ifm
+ 1);
662 flags
= ifm
->ifm_flags
;
665 if ((flags
& IFF_UP
) == 0 ||
666 (flags
& (((multicast_mode
== PER_INTERFACE_MULTICAST
) ?
668 IFF_BROADCAST
|iff_flag
)) == 0)
670 if (ifm
->ifm_type
!= RTM_NEWADDR
)
671 quit("out of sync parsing NET_RT_IFLIST", WITHOUT_ERRNO
);
672 ifam
= (struct ifa_msghdr
*)ifm
;
673 info
.rti_addrs
= ifam
->ifam_addrs
;
674 rt_xaddrs((char *)(ifam
+ 1), ifam
->ifam_msglen
+ (char *)ifam
,
676 /* gag, wish we could get rid of Internet dependencies */
677 #define dstaddr info.rti_info[RTAX_BRD]
678 #define ifaddr info.rti_info[RTAX_IFA]
679 #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
680 #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
681 if (dstaddr
== 0 || dstaddr
->sa_family
!= AF_INET
)
683 PORT_SA(dstaddr
) = sp
->s_port
;
684 for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
)
685 if (memcmp(sdl
->sdl_data
, np
->n_name
,
686 sdl
->sdl_nlen
) == 0 &&
687 IPADDR_SA(np
->n_addr
) == IPADDR_SA(dstaddr
))
691 len
= sizeof(*np
) + dstaddr
->sa_len
+ sdl
->sdl_nlen
+ 1;
692 np
= (struct neighbor
*)malloc(len
);
694 quit("malloc of neighbor structure", WITH_ERRNO
);
697 np
->n_addr
= (struct sockaddr
*)(np
+ 1);
698 np
->n_addrlen
= dstaddr
->sa_len
;
699 np
->n_name
= np
->n_addrlen
+ (char *)np
->n_addr
;
700 memcpy((char *)np
->n_addr
, (char *)dstaddr
, np
->n_addrlen
);
701 memcpy(np
->n_name
, sdl
->sdl_data
, sdl
->sdl_nlen
);
702 if (multicast_mode
== PER_INTERFACE_MULTICAST
&&
703 (flags
& IFF_MULTICAST
) &&
704 !(flags
& IFF_LOOPBACK
)) {
707 memcpy((char *)np
->n_addr
, (char *)ifaddr
,
709 mreq
.imr_multiaddr
.s_addr
= htonl(INADDR_WHOD_GROUP
);
710 mreq
.imr_interface
.s_addr
=
711 ((struct sockaddr_in
*)np
->n_addr
)->sin_addr
.s_addr
;
712 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
713 &mreq
, sizeof(mreq
)) < 0) {
715 "setsockopt IP_ADD_MEMBERSHIP: %m");
717 /* Fall back to broadcast on this if. */
718 np
->n_flags
&= ~IFF_MULTICAST
;
725 np
->n_next
= neighbors
;
734 Sendto(int s_debug
, const void *buf
, size_t cc
, int flags
,
735 const struct sockaddr
*to
, int tolen
)
737 const struct whod
*w
= (const struct whod
*)buf
;
738 const struct whoent
*we
;
739 const struct sockaddr_in
*sock_in
= (const struct sockaddr_in
*)to
;
743 ret
= sendto(s_debug
, buf
, cc
, flags
, to
, tolen
);
745 printf("sendto %s:%d\n", inet_ntoa(sock_in
->sin_addr
),
746 ntohs(sock_in
->sin_port
));
747 printf("hostname %s %s\n", w
->wd_hostname
,
748 interval(ntohl(w
->wd_sendtime
) - ntohl(w
->wd_boottime
), " up"));
749 printf("load %4.2f, %4.2f, %4.2f\n",
750 ntohl(w
->wd_loadav
[0]) / 100.0, ntohl(w
->wd_loadav
[1]) / 100.0,
751 ntohl(w
->wd_loadav
[2]) / 100.0);
753 for (we
= w
->wd_we
, cc
/= sizeof(struct whoent
); cc
> 0; cc
--, we
++) {
754 time_t t
= ntohl(we
->we_utmp
.out_time
);
755 idle_time
= we
->we_idle
;
756 printf("%-8.8s %s:%s %.12s",
757 we
->we_utmp
.out_name
,
758 w
->wd_hostname
, we
->we_utmp
.out_line
,
760 idle_time
= ntohl(we
->we_idle
) / 60;
762 if (idle_time
>= 100*60)
763 idle_time
= 100*60 - 1;
765 printf(" %2d", idle_time
/ 60);
768 printf(":%02d", idle_time
% 60);
776 interval(int inter_time
, const char *updown
)
778 static char resbuf
[32];
779 int days
, hours
, minutes
;
781 if (inter_time
< 0 || inter_time
> 3*30*24*60*60) {
782 snprintf(resbuf
, sizeof(resbuf
), " %s ??:??", updown
);
785 minutes
= (inter_time
+ 59) / 60; /* round to minutes */
786 hours
= minutes
/ 60; minutes
%= 60;
787 days
= hours
/ 24; hours
%= 24;
789 snprintf(resbuf
, sizeof(resbuf
), "%s %2d+%02d:%02d",
790 updown
, days
, hours
, minutes
);
792 snprintf(resbuf
, sizeof(resbuf
), "%s %2d:%02d",
793 updown
, hours
, minutes
);