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. 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) 1983, 1993 The Regents of the University of California. All rights reserved.
30 * @(#)rwhod.c 8.1 (Berkeley) 6/6/93
31 * $FreeBSD: src/usr.sbin/rwhod/rwhod.c,v 1.13.2.2 2000/12/23 15:28:12 iedowse Exp $
34 #include <sys/param.h>
35 #include <sys/socket.h>
37 #include <sys/signal.h>
38 #include <sys/ioctl.h>
39 #include <sys/sysctl.h>
42 #include <net/if_dl.h>
43 #include <net/route.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <protocols/rwhod.h>
66 * This version of Berkeley's rwhod has been modified to use IP multicast
67 * datagrams, under control of a new command-line option:
69 * rwhod -m causes rwhod to use IP multicast (instead of
70 * broadcast or unicast) on all interfaces that have
71 * the IFF_MULTICAST flag set in their "ifnet" structs
72 * (excluding the loopback interface). The multicast
73 * reports are sent with a time-to-live of 1, to prevent
74 * forwarding beyond the directly-connected subnet(s).
76 * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a
77 * time-to-live of <ttl>, via a SINGLE interface rather
78 * than all interfaces. <ttl> must be between 0 and
79 * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1"
80 * is different than "-m", in that "-m 1" specifies
81 * transmission on one interface only.
83 * When "-m" is used without a <ttl> argument, the program accepts multicast
84 * rwhod reports from all multicast-capable interfaces. If a <ttl> argument
85 * is given, it accepts multicast reports from only one interface, the one
86 * on which reports are sent (which may be controlled via the host's routing
87 * table). Regardless of the "-m" option, the program accepts broadcast or
88 * unicast reports from all interfaces. Thus, this program will hear the
89 * reports of old, non-multicasting rwhods, but, if multicasting is used,
90 * those old rwhods won't hear the reports generated by this program.
92 * -- Steve Deering, Stanford University, February 1989
95 #define UNPRIV_USER "daemon"
96 #define UNPRIV_GROUP "daemon"
98 #define WITH_ERRNO 1 /* Write to syslog with errno (%m) */
99 #define WITHOUT_ERRNO 0 /* Write to syslog without errno */
101 #define NO_MULTICAST 0 /* multicast modes */
102 #define PER_INTERFACE_MULTICAST 1
103 #define SCOPED_MULTICAST 2
105 #define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */
107 #define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */
108 /* (belongs in protocols/rwhod.h) */
112 int iff_flag
= IFF_POINTOPOINT
;
113 int multicast_mode
= NO_MULTICAST
;
115 struct sockaddr_in multicast_addr
;
117 char myname
[MAXHOSTNAMELEN
];
120 * We communicate with each neighbor in a list constructed at the time we're
121 * started up. Neighbors are currently directly connected via a hardware
125 struct neighbor
*n_next
;
126 char *n_name
; /* interface name */
127 struct sockaddr
*n_addr
; /* who to send to */
128 int n_addrlen
; /* size of address */
129 int n_flags
; /* should forward?, interface flags */
132 struct neighbor
*neighbors
;
136 volatile sig_atomic_t onsighup
;
138 #define WHDRSIZE (sizeof(mywd) - sizeof(mywd.wd_we))
140 void run_as(uid_t
*, gid_t
*);
142 void getboottime(void);
143 void send_host_information(void);
145 void quit(const char *, int);
146 void rt_xaddrs(caddr_t
, caddr_t
, struct rt_addrinfo
*);
147 int verify(char *, int);
148 void handleread(int);
149 static void usage(void);
150 void timeadd(struct timeval
*, time_t, struct timeval
*);
153 char *interval(int, const char *);
154 ssize_t
Sendto(int, const void *, size_t, int,
155 const struct sockaddr
*, int);
157 #define Sendto sendto
161 main(int argc
, char *argv
[])
163 struct pollfd pfd
[1];
166 int send_time
= 180; /* Default time, 180 seconds (3 minutes) */
167 struct sockaddr_in m_sin
;
172 struct timeval next
= { .tv_sec
= 0 }, now
;
175 errx(1, "not super user");
177 run_as(&unpriv_uid
, &unpriv_gid
);
180 while (argc
> 0 && *argv
[0] == '-') {
181 if (strcmp(*argv
, "-m") == 0) {
182 if (argc
> 1 && *(argv
+ 1)[0] != '-') {
183 /* Argument has been given */
185 multicast_mode
= SCOPED_MULTICAST
;
186 tmp
= strtol(*argv
, &ep
, 10);
187 if (*ep
!= '\0' || tmp
< INT_MIN
|| tmp
> INT_MAX
)
188 errx(1, "invalid ttl: %s", *argv
);
189 multicast_scope
= (int)tmp
;
190 if (multicast_scope
> MAX_MULTICAST_SCOPE
)
191 errx(1, "ttl must not exceed %u",
192 MAX_MULTICAST_SCOPE
);
194 else multicast_mode
= PER_INTERFACE_MULTICAST
;
195 } else if (strcmp(*argv
, "-g") == 0) {
196 if (argc
> 1 && *(argv
+ 1)[0] != '-') {
198 send_time
= (int)strtol(*argv
, &ep
, 10);
200 errx(1, "time must be greater than 0");
204 errx(1, "invalid argument: %s", *argv
);
205 if (*ep
== 'M' || *ep
== 'm') {
206 /* Time in minutes. */
209 errx(1, "invalid argument: %s", *argv
);
213 errx(1, "cannot be greater than 180 seconds (3 minutes)");
215 errx(1, "missing argument");
216 } else if (strcmp(*argv
, "-i") == 0)
218 else if (strcmp(*argv
, "-l") == 0)
220 else if (strcmp(*argv
, "-p") == 0)
229 struct pidfh
*pfh
= NULL
;
230 pfh
= pidfile_open(NULL
, 600, NULL
);
235 openlog("rwhod", LOG_PID
, LOG_DAEMON
);
236 sp
= getservbyname("who", "udp");
238 quit("udp/who: unknown service", WITHOUT_ERRNO
);
240 if (chdir(_PATH_RWHODIR
) < 0)
241 quit(_PATH_RWHODIR
, WITH_ERRNO
);
244 * Establish host name as returned by system.
246 if (gethostname(myname
, sizeof(myname
)) < 0)
247 quit("gethostname", WITH_ERRNO
);
249 if ((cp
= strchr(myname
, '.')) != NULL
)
251 strlcpy(mywd
.wd_hostname
, myname
, sizeof(mywd
.wd_hostname
));
252 utmpf
= open(_PATH_UTMP
, O_RDONLY
|O_CREAT
, 0644);
254 quit(_PATH_UTMP
, WITH_ERRNO
);
257 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
258 quit("socket", WITH_ERRNO
);
260 if (setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &on
, sizeof(on
)) < 0)
261 quit("setsockopt SO_BROADCAST", WITH_ERRNO
);
263 memset(&m_sin
, 0, sizeof(m_sin
));
264 m_sin
.sin_len
= sizeof(m_sin
);
265 m_sin
.sin_family
= AF_INET
;
266 m_sin
.sin_port
= sp
->s_port
;
267 if (bind(s
, (struct sockaddr
*)&m_sin
, sizeof(m_sin
)) < 0)
268 quit("bind", WITH_ERRNO
);
271 setgroups(1, &unpriv_gid
); /* XXX BOGUS groups[0] = egid */
277 send_host_information();
279 gettimeofday(&now
, NULL
);
280 timeadd(&now
, delta
, &next
);
284 pfd
[0].events
= POLLIN
;
289 n
= poll(pfd
, 1, 1000);
299 gettimeofday(&now
, NULL
);
300 if (now
.tv_sec
> next
.tv_sec
) {
301 send_host_information();
302 timeadd(&now
, delta
, &next
);
309 timeadd(struct timeval
*now
, time_t delta
, struct timeval
*next
)
311 (next
)->tv_sec
= (now
)->tv_sec
+ delta
;
317 struct sockaddr_in from
;
322 socklen_t len
= sizeof(from
);
324 cc
= recvfrom(sock
, (char *)&wd
, sizeof(struct whod
), 0,
325 (struct sockaddr
*)&from
, &len
);
329 if (cc
< 0 && errno
!= EINTR
) {
330 syslog(LOG_WARNING
, "recv: %m");
334 if (from
.sin_port
!= sp
->s_port
&& !insecure_mode
) {
335 syslog(LOG_WARNING
, "%d: bad source port from %s",
336 ntohs(from
.sin_port
), inet_ntoa(from
.sin_addr
));
339 if (cc
< (int)WHDRSIZE
) {
340 syslog(LOG_WARNING
, "short packet from %s",
341 inet_ntoa(from
.sin_addr
));
344 if (wd
.wd_vers
!= WHODVERSION
)
346 if (wd
.wd_type
!= WHODTYPE_STATUS
)
348 if (!verify(wd
.wd_hostname
, sizeof wd
.wd_hostname
)) {
349 syslog(LOG_WARNING
, "malformed host name from %s",
350 inet_ntoa(from
.sin_addr
));
353 snprintf(path
, sizeof path
, "whod.%s", wd
.wd_hostname
);
355 * Rather than truncating and growing the file each time,
356 * use ftruncate if size is less than previous size.
358 whod
= open(path
, O_WRONLY
| O_CREAT
, 0644);
360 syslog(LOG_WARNING
, "%s: %m", path
);
363 #if ENDIAN != BIG_ENDIAN
365 int i
, n
= (cc
- WHDRSIZE
)/sizeof(struct whoent
);
368 /* undo header byte swapping before writing to file */
369 wd
.wd_sendtime
= ntohl(wd
.wd_sendtime
);
370 for (i
= 0; i
< 3; i
++)
371 wd
.wd_loadav
[i
] = ntohl(wd
.wd_loadav
[i
]);
372 wd
.wd_boottime
= ntohl(wd
.wd_boottime
);
374 for (i
= 0; i
< n
; i
++) {
375 we
->we_idle
= ntohl(we
->we_idle
);
376 we
->we_utmp
.out_time
=
377 ntohl(we
->we_utmp
.out_time
);
382 wd
.wd_recvtime
= time(NULL
);
383 write(whod
, (char *)&wd
, cc
);
384 if (fstat(whod
, &st
) < 0 || st
.st_size
> cc
)
392 fprintf(stderr
, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n");
397 hup(int signo __unused
)
403 run_as(uid_t
*uid
, gid_t
*gid
)
408 pw
= getpwnam(UNPRIV_USER
);
410 quit("getpwnam(daemon)", WITH_ERRNO
);
414 gr
= getgrnam(UNPRIV_GROUP
);
416 quit("getgrnam(daemon)", WITH_ERRNO
);
422 * Check out host name for unprintables
423 * and other funnies before allowing a file
424 * to be created. Sorry, but blanks aren't allowed.
427 verify(char *name
, int maxlen
)
431 while (*name
&& size
< maxlen
- 1) {
432 if (!isascii(*name
) || !(isalnum(*name
) || ispunct(*name
)))
447 send_host_information(void)
450 struct whoent
*we
= mywd
.wd_we
, *wlast
;
458 if (alarmcount
% 10 == 0)
462 if ((stb
.st_mtime
!= utmptime
) || (stb
.st_size
> utmpsize
)) {
463 utmptime
= stb
.st_mtime
;
464 if (stb
.st_size
> utmpsize
) {
465 utmpsize
= stb
.st_size
+ 10 * sizeof(struct utmp
);
466 utmp
= reallocf(utmp
, utmpsize
);
468 syslog(LOG_WARNING
, "malloc failed: %m");
473 lseek(utmpf
, (off_t
)0, L_SET
);
474 cc
= read(utmpf
, (char *)utmp
, stb
.st_size
);
476 syslog(LOG_ERR
, "read(%s): %m", _PATH_UTMP
);
479 wlast
= &mywd
.wd_we
[1024 / sizeof(struct whoent
) - 1];
480 utmpent
= cc
/ sizeof(struct utmp
);
481 for (i
= 0; i
< utmpent
; i
++)
482 if (utmp
[i
].ut_name
[0]) {
483 memcpy(we
->we_utmp
.out_line
, utmp
[i
].ut_line
,
484 sizeof(utmp
[i
].ut_line
));
485 memcpy(we
->we_utmp
.out_name
, utmp
[i
].ut_name
,
486 sizeof(utmp
[i
].ut_name
));
487 we
->we_utmp
.out_time
= htonl(utmp
[i
].ut_time
);
492 utmpent
= we
- mywd
.wd_we
;
496 * The test on utmpent looks silly---after all, if no one is
497 * logged on, why worry about efficiency?---but is useful on
498 * (e.g.) compute servers.
500 if (utmpent
&& chdir(_PATH_DEV
)) {
501 syslog(LOG_ERR
, "chdir(%s): %m", _PATH_DEV
);
506 for (i
= 0; i
< utmpent
; i
++) {
507 if (stat(we
->we_utmp
.out_line
, &stb
) >= 0)
508 we
->we_idle
= htonl(now
- stb
.st_atime
);
511 getloadavg(avenrun
, sizeof(avenrun
)/sizeof(avenrun
[0]));
512 for (i
= 0; i
< 3; i
++)
513 mywd
.wd_loadav
[i
] = htonl((u_long
)(avenrun
[i
] * 100));
514 cc
= (char *)we
- (char *)&mywd
;
515 mywd
.wd_sendtime
= htonl(time(0));
516 mywd
.wd_vers
= WHODVERSION
;
517 mywd
.wd_type
= WHODTYPE_STATUS
;
518 if (multicast_mode
== SCOPED_MULTICAST
) {
519 Sendto(s
, (char *)&mywd
, cc
, 0,
520 (struct sockaddr
*)&multicast_addr
,
521 sizeof(multicast_addr
));
523 else for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
) {
524 if (multicast_mode
== PER_INTERFACE_MULTICAST
&&
525 np
->n_flags
& IFF_MULTICAST
) {
527 * Select the outgoing interface for the multicast.
529 if (setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
,
530 &(((struct sockaddr_in
*)np
->n_addr
)->sin_addr
),
531 sizeof(struct in_addr
)) < 0) {
532 quit("setsockopt IP_MULTICAST_IF", WITH_ERRNO
);
534 Sendto(s
, (char *)&mywd
, cc
, 0,
535 (struct sockaddr
*)&multicast_addr
,
536 sizeof(multicast_addr
));
537 } else Sendto(s
, (char *)&mywd
, cc
, 0,
538 np
->n_addr
, np
->n_addrlen
);
540 if (utmpent
&& chdir(_PATH_RWHODIR
)) {
541 syslog(LOG_ERR
, "chdir(%s): %m", _PATH_RWHODIR
);
554 mib
[1] = KERN_BOOTTIME
;
556 if (sysctl(mib
, 2, &tm
, &size
, NULL
, 0) == -1)
557 quit("cannot get boottime", WITH_ERRNO
);
559 mywd
.wd_boottime
= htonl(tm
.tv_sec
);
563 * If wrterrno == WITH_ERRNO, we will print
564 * errno. If not, we leave errno out.
567 quit(const char *msg
, int wrterrno
)
570 syslog(LOG_ERR
, "%s: %m", msg
);
572 syslog(LOG_ERR
, "%s", msg
);
577 rt_xaddrs(caddr_t cp
, caddr_t cplim
, struct rt_addrinfo
*rtinfo
)
582 memset(rtinfo
->rti_info
, 0, sizeof(rtinfo
->rti_info
));
583 for (i
= 0; (i
< RTAX_MAX
) && (cp
< cplim
); i
++) {
584 if ((rtinfo
->rti_addrs
& (1 << i
)) == 0)
586 rtinfo
->rti_info
[i
] = sa
= (struct sockaddr
*)cp
;
592 * Figure out device configuration and select
593 * networks which deserve status information.
596 configure(int c_sock
)
599 struct if_msghdr
*ifm
;
600 struct ifa_msghdr
*ifam
;
601 struct sockaddr_dl
*sdl
;
603 int mib
[6], flags
= 0, len
;
604 char *buf
, *lim
, *next
;
605 struct rt_addrinfo info
;
607 if (multicast_mode
!= NO_MULTICAST
) {
608 multicast_addr
.sin_len
= sizeof(multicast_addr
);
609 multicast_addr
.sin_family
= AF_INET
;
610 multicast_addr
.sin_addr
.s_addr
= htonl(INADDR_WHOD_GROUP
);
611 multicast_addr
.sin_port
= sp
->s_port
;
614 if (multicast_mode
== SCOPED_MULTICAST
) {
618 mreq
.imr_multiaddr
.s_addr
= htonl(INADDR_WHOD_GROUP
);
619 mreq
.imr_interface
.s_addr
= htonl(INADDR_ANY
);
620 if (setsockopt(c_sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
621 &mreq
, sizeof(mreq
)) < 0) {
623 "setsockopt IP_ADD_MEMBERSHIP: %m");
626 ttl
= multicast_scope
;
627 if (setsockopt(c_sock
, IPPROTO_IP
, IP_MULTICAST_TTL
,
628 &ttl
, sizeof(ttl
)) < 0) {
630 "setsockopt IP_MULTICAST_TTL: %m");
640 mib
[4] = NET_RT_IFLIST
;
642 if (sysctl(mib
, 6, NULL
, &needed
, NULL
, 0) < 0)
643 quit("route-sysctl-estimate", WITH_ERRNO
);
644 if ((buf
= malloc(needed
)) == NULL
)
645 quit("malloc", WITH_ERRNO
);
646 if (sysctl(mib
, 6, buf
, &needed
, NULL
, 0) < 0)
647 quit("actual retrieval of interface table", WITH_ERRNO
);
650 sdl
= NULL
; /* XXX just to keep gcc -Wall happy */
651 for (next
= buf
; next
< lim
; next
+= ifm
->ifm_msglen
) {
652 ifm
= (struct if_msghdr
*)next
;
653 if (ifm
->ifm_type
== RTM_IFINFO
) {
654 sdl
= (struct sockaddr_dl
*)(ifm
+ 1);
655 flags
= ifm
->ifm_flags
;
658 if ((flags
& IFF_UP
) == 0 ||
659 (flags
& (((multicast_mode
== PER_INTERFACE_MULTICAST
) ?
661 IFF_BROADCAST
|iff_flag
)) == 0)
663 if (ifm
->ifm_type
!= RTM_NEWADDR
)
664 quit("out of sync parsing NET_RT_IFLIST", WITHOUT_ERRNO
);
665 ifam
= (struct ifa_msghdr
*)ifm
;
666 info
.rti_addrs
= ifam
->ifam_addrs
;
667 rt_xaddrs((char *)(ifam
+ 1), ifam
->ifam_msglen
+ (char *)ifam
,
669 /* gag, wish we could get rid of Internet dependencies */
670 #define dstaddr info.rti_info[RTAX_BRD]
671 #define ifaddr info.rti_info[RTAX_IFA]
672 #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
673 #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
674 if (dstaddr
== 0 || dstaddr
->sa_family
!= AF_INET
)
676 PORT_SA(dstaddr
) = sp
->s_port
;
677 for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
)
678 if (memcmp(sdl
->sdl_data
, np
->n_name
,
679 sdl
->sdl_nlen
) == 0 &&
680 IPADDR_SA(np
->n_addr
) == IPADDR_SA(dstaddr
))
684 len
= sizeof(*np
) + dstaddr
->sa_len
+ sdl
->sdl_nlen
+ 1;
685 np
= (struct neighbor
*)malloc(len
);
687 quit("malloc of neighbor structure", WITH_ERRNO
);
690 np
->n_addr
= (struct sockaddr
*)(np
+ 1);
691 np
->n_addrlen
= dstaddr
->sa_len
;
692 np
->n_name
= np
->n_addrlen
+ (char *)np
->n_addr
;
693 memcpy((char *)np
->n_addr
, (char *)dstaddr
, np
->n_addrlen
);
694 memcpy(np
->n_name
, sdl
->sdl_data
, sdl
->sdl_nlen
);
695 if (multicast_mode
== PER_INTERFACE_MULTICAST
&&
696 (flags
& IFF_MULTICAST
) &&
697 !(flags
& IFF_LOOPBACK
)) {
700 memcpy((char *)np
->n_addr
, (char *)ifaddr
,
702 mreq
.imr_multiaddr
.s_addr
= htonl(INADDR_WHOD_GROUP
);
703 mreq
.imr_interface
.s_addr
=
704 ((struct sockaddr_in
*)np
->n_addr
)->sin_addr
.s_addr
;
705 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
706 &mreq
, sizeof(mreq
)) < 0) {
708 "setsockopt IP_ADD_MEMBERSHIP: %m");
710 /* Fall back to broadcast on this if. */
711 np
->n_flags
&= ~IFF_MULTICAST
;
718 np
->n_next
= neighbors
;
727 Sendto(int s_debug
, const void *buf
, size_t cc
, int flags
,
728 const struct sockaddr
*to
, int tolen
)
730 const struct whod
*w
= (const struct whod
*)buf
;
731 const struct whoent
*we
;
732 const struct sockaddr_in
*sock_in
= (const struct sockaddr_in
*)to
;
736 ret
= sendto(s_debug
, buf
, cc
, flags
, to
, tolen
);
738 printf("sendto %s:%d\n", inet_ntoa(sock_in
->sin_addr
),
739 ntohs(sock_in
->sin_port
));
740 printf("hostname %s %s\n", w
->wd_hostname
,
741 interval(ntohl(w
->wd_sendtime
) - ntohl(w
->wd_boottime
), " up"));
742 printf("load %4.2f, %4.2f, %4.2f\n",
743 ntohl(w
->wd_loadav
[0]) / 100.0, ntohl(w
->wd_loadav
[1]) / 100.0,
744 ntohl(w
->wd_loadav
[2]) / 100.0);
746 for (we
= w
->wd_we
, cc
/= sizeof(struct whoent
); cc
> 0; cc
--, we
++) {
747 time_t t
= ntohl(we
->we_utmp
.out_time
);
748 idle_time
= we
->we_idle
;
749 printf("%-8.8s %s:%s %.12s",
750 we
->we_utmp
.out_name
,
751 w
->wd_hostname
, we
->we_utmp
.out_line
,
753 idle_time
= ntohl(we
->we_idle
) / 60;
755 if (idle_time
>= 100*60)
756 idle_time
= 100*60 - 1;
758 printf(" %2d", idle_time
/ 60);
761 printf(":%02d", idle_time
% 60);
769 interval(int inter_time
, const char *updown
)
771 static char resbuf
[32];
772 int days
, hours
, minutes
;
774 if (inter_time
< 0 || inter_time
> 3*30*24*60*60) {
775 snprintf(resbuf
, sizeof(resbuf
), " %s ??:??", updown
);
778 minutes
= (inter_time
+ 59) / 60; /* round to minutes */
779 hours
= minutes
/ 60; minutes
%= 60;
780 days
= hours
/ 24; hours
%= 24;
782 snprintf(resbuf
, sizeof(resbuf
), "%s %2d+%02d:%02d",
783 updown
, days
, hours
, minutes
);
785 snprintf(resbuf
, sizeof(resbuf
), "%s %2d:%02d",
786 updown
, hours
, minutes
);