1 /* $NetBSD: rwhod.c,v 1.37 2008/07/21 13:36:59 lukem Exp $ */
4 * Copyright (c) 1983, 1993
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) 1983, 1993\
35 The Regents of the University of California. All rights reserved.");
40 static char sccsid
[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93";
42 __RCSID("$NetBSD: rwhod.c,v 1.37 2008/07/21 13:36:59 lukem Exp $");
46 #include <sys/param.h>
47 #include <sys/socket.h>
49 #include <sys/signal.h>
50 #include <sys/ioctl.h>
51 #include <sys/sysctl.h>
54 #include <net/if_dl.h>
55 #include <net/route.h>
56 #include <netinet/in.h>
57 #include <protocols/rwhod.h>
58 #include <arpa/inet.h>
74 #include "utmpentry.h"
76 #define CHECK_INTERVAL (3 * 60)
78 /* Time interval limit; ruptime will think that we are down > than this */
79 #define MAX_INTERVAL (11 * 60)
82 static char myname
[MAXHOSTNAMELEN
+ 1];
85 * We communicate with each neighbor in a list constructed at the time we're
86 * started up. Neighbors are currently directly connected via a hardware
90 struct neighbor
*n_next
;
91 char *n_name
; /* interface name */
92 struct sockaddr
*n_addr
; /* who to send to */
93 int n_addrlen
; /* size of address */
94 int n_flags
; /* should forward?, interface flags */
97 static struct neighbor
*neighbors
;
98 static struct whod mywd
;
99 static struct servent
*sp
;
100 static volatile sig_atomic_t onsighup
;
102 #define WHDRSIZE (sizeof(mywd) - sizeof(mywd.wd_we))
104 static int configure(int);
105 static void getboottime(void);
106 static void send_host_information(int);
107 static void sighup(int);
108 static void handleread(int);
109 static void quit(const char *);
110 static void rt_xaddrs(void *, void *, struct rt_addrinfo
*);
111 static int drop_privs(char *);
112 static void usage(void) __dead
;
113 static int verify(const char *);
115 static char *interval(int, const char *);
116 static ssize_t
Sendto(int, const void *, size_t, int,
117 const struct sockaddr
*, socklen_t
);
119 #define Sendto sendto
123 main(int argc
, char *argv
[])
126 int time_interval
= 180; /* Default time (180 seconds) */
129 struct sockaddr_in sasin
;
130 struct pollfd pfd
[1];
131 struct timeval delta
, next
, now
;
132 char *newuser
= NULL
;
134 setprogname(argv
[0]);
137 errx(EXIT_FAILURE
, "not super user");
139 while ((ch
= getopt(argc
, argv
, "i:u:")) != -1) {
142 time_interval
= (int)strtol(optarg
, &ep
, 10);
149 /* Time in minutes. */
155 errx(1, "Invalid argument: `%s'", optarg
);
158 if (time_interval
<= 0)
159 errx(1, "Interval must be greater than 0");
161 if (time_interval
> MAX_INTERVAL
)
162 errx(1, "Interval cannot be greater than"
163 " %d minutes", MAX_INTERVAL
/ 60);
175 sp
= getservbyname("who", "udp");
177 errx(EXIT_FAILURE
, "udp/who: unknown service");
182 if (chdir(_PATH_RWHODIR
) < 0)
183 err(EXIT_FAILURE
, "%s", _PATH_RWHODIR
);
184 (void)signal(SIGHUP
, sighup
);
185 openlog(getprogname(), LOG_PID
, LOG_DAEMON
);
187 * Establish host name as returned by system.
189 if (gethostname(myname
, sizeof(myname
) - 1) < 0) {
190 syslog(LOG_ERR
, "gethostname: %m");
193 myname
[sizeof(myname
) - 1] = '\0';
194 if ((cp
= strchr(myname
, '.')) != NULL
)
196 (void)strncpy(mywd
.wd_hostname
, myname
, sizeof(mywd
.wd_hostname
) - 1);
198 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
199 syslog(LOG_ERR
, "socket: %m");
202 if (setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &on
, sizeof(on
)) < 0) {
203 syslog(LOG_ERR
, "setsockopt SO_BROADCAST: %m");
206 (void)memset(&sasin
, 0, sizeof(sasin
));
207 sasin
.sin_family
= AF_INET
;
208 sasin
.sin_port
= sp
->s_port
;
209 if (bind(s
, (struct sockaddr
*)&sasin
, sizeof(sasin
)) < 0) {
210 syslog(LOG_ERR
, "bind: %m");
217 if (!drop_privs(newuser
))
220 send_host_information(s
);
221 delta
.tv_sec
= time_interval
;
223 gettimeofday(&now
, NULL
);
224 timeradd(&now
, &delta
, &next
);
227 pfd
[0].events
= POLLIN
;
232 n
= poll(pfd
, 1, 1000);
242 (void)gettimeofday(&now
, NULL
);
243 if (timercmp(&now
, &next
, >)) {
244 send_host_information(s
);
245 timeradd(&now
, &delta
, &next
);
254 sighup(int signo __unused
)
262 struct sockaddr_in from
;
267 socklen_t len
= sizeof(from
);
269 cc
= recvfrom(s
, (char *)&wd
, sizeof(struct whod
), 0,
270 (struct sockaddr
*)&from
, &len
);
272 if (cc
< 0 && errno
!= EINTR
)
273 syslog(LOG_WARNING
, "recv: %m");
276 if (from
.sin_port
!= sp
->s_port
) {
277 syslog(LOG_WARNING
, "%d: bad from port",
278 ntohs(from
.sin_port
));
281 if (cc
< (int)WHDRSIZE
) {
282 syslog(LOG_WARNING
, "Short packet from %s",
283 inet_ntoa(from
.sin_addr
));
287 if (wd
.wd_vers
!= WHODVERSION
)
289 if (wd
.wd_type
!= WHODTYPE_STATUS
)
292 * Ensure null termination of the name within the packet.
293 * Otherwise we might overflow or read past the end.
295 wd
.wd_hostname
[sizeof(wd
.wd_hostname
)-1] = 0;
296 if (!verify(wd
.wd_hostname
)) {
297 syslog(LOG_WARNING
, "malformed host name from %s",
298 inet_ntoa(from
.sin_addr
));
301 (void)snprintf(path
, sizeof(path
), "whod.%s", wd
.wd_hostname
);
303 * Rather than truncating and growing the file each time,
304 * use ftruncate if size is less than previous size.
306 whod
= open(path
, O_WRONLY
| O_CREAT
, 0644);
308 syslog(LOG_WARNING
, "%s: %m", path
);
311 #if ENDIAN != BIG_ENDIAN
313 int i
, n
= (cc
- WHDRSIZE
) / sizeof(struct whoent
);
316 /* undo header byte swapping before writing to file */
317 wd
.wd_sendtime
= ntohl(wd
.wd_sendtime
);
318 for (i
= 0; i
< 3; i
++)
319 wd
.wd_loadav
[i
] = ntohl(wd
.wd_loadav
[i
]);
320 wd
.wd_boottime
= ntohl(wd
.wd_boottime
);
322 for (i
= 0; i
< n
; i
++) {
323 we
->we_idle
= ntohl(we
->we_idle
);
324 we
->we_utmp
.out_time
=
325 ntohl(we
->we_utmp
.out_time
);
330 wd
.wd_recvtime
= time(NULL
);
331 (void)write(whod
, (char *)&wd
, cc
);
332 if (fstat(whod
, &st
) < 0 || st
.st_size
> cc
)
333 (void)ftruncate(whod
, cc
);
338 * Check out host name for unprintables
339 * and other funnies before allowing a file
340 * to be created. Sorry, but blanks aren't allowed.
343 verify(const char *name
)
348 if (!isascii((unsigned char)*name
) ||
349 !(isalnum((unsigned char)*name
) ||
350 ispunct((unsigned char)*name
)))
358 send_host_information(int s
)
361 struct whoent
*we
= mywd
.wd_we
, *wlast
;
362 int i
, cc
, utmpent
= 0;
366 static struct utmpentry
*ohead
= NULL
;
367 struct utmpentry
*ep
;
368 static int count
= 0;
375 (void)getutentries(NULL
, &ep
);
376 /* XXX probably should expose utmp mtime, check that instead */
378 wlast
= &mywd
.wd_we
[1024 / sizeof(struct whoent
) - 1];
379 for (; ep
; ep
= ep
->next
) {
380 (void)strncpy(we
->we_utmp
.out_line
, ep
->line
,
381 sizeof(we
->we_utmp
.out_line
));
382 (void)strncpy(we
->we_utmp
.out_name
, ep
->name
,
383 sizeof(we
->we_utmp
.out_name
));
384 we
->we_utmp
.out_time
= htonl(ep
->tv
.tv_sec
);
389 utmpent
= we
- mywd
.wd_we
;
393 * The test on utmpent looks silly---after all, if no one is
394 * logged on, why worry about efficiency?---but is useful on
395 * (e.g.) compute servers.
397 if (utmpent
&& chdir(_PATH_DEV
)) {
398 syslog(LOG_ERR
, "chdir(%s): %m", _PATH_DEV
);
402 for (i
= 0; i
< utmpent
; i
++) {
403 if (stat(we
->we_utmp
.out_line
, &stb
) >= 0)
404 we
->we_idle
= htonl(now
- stb
.st_atime
);
407 (void)getloadavg(avenrun
, sizeof(avenrun
)/sizeof(avenrun
[0]));
408 for (i
= 0; i
< 3; i
++)
409 mywd
.wd_loadav
[i
] = htonl((u_long
)(avenrun
[i
] * 100));
410 cc
= (char *)we
- (char *)&mywd
;
411 mywd
.wd_sendtime
= htonl(time(0));
412 mywd
.wd_vers
= WHODVERSION
;
413 mywd
.wd_type
= WHODTYPE_STATUS
;
414 for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
)
415 (void)Sendto(s
, (char *)&mywd
, cc
, 0,
416 np
->n_addr
, np
->n_addrlen
);
417 if (utmpent
&& chdir(_PATH_RWHODIR
)) {
418 syslog(LOG_ERR
, "chdir(%s): %m", _PATH_RWHODIR
);
431 mib
[1] = KERN_BOOTTIME
;
433 if (sysctl(mib
, 2, &tm
, &size
, NULL
, 0) == -1) {
434 syslog(LOG_ERR
, "cannot get boottime: %m");
437 mywd
.wd_boottime
= htonl(tm
.tv_sec
);
441 quit(const char *msg
)
443 syslog(LOG_ERR
, "%s", msg
);
448 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
449 #define ADVANCE(x, n) ((char *)(x) + ROUNDUP((n)->sa_len))
452 rt_xaddrs(void *cp
, void *cplim
, struct rt_addrinfo
*rtinfo
)
457 (void)memset(rtinfo
->rti_info
, 0, sizeof(rtinfo
->rti_info
));
458 for (i
= 0; (i
< RTAX_MAX
) && (cp
< cplim
); i
++) {
459 if ((rtinfo
->rti_addrs
& (1 << i
)) == 0)
461 rtinfo
->rti_info
[i
] = sa
= (struct sockaddr
*)cp
;
462 cp
= ADVANCE(cp
, sa
);
467 * Figure out device configuration and select
468 * networks which deserve status information.
474 struct if_msghdr
*ifm
;
475 struct ifa_msghdr
*ifam
;
476 struct sockaddr_dl
*sdl
;
478 int mib
[6], flags
= 0, len
;
479 char *buf
, *lim
, *next
;
480 struct rt_addrinfo info
;
481 struct sockaddr_in dstaddr
;
487 mib
[4] = NET_RT_IFLIST
;
489 if (sysctl(mib
, 6, NULL
, &needed
, NULL
, 0) < 0)
490 quit("route-sysctl-estimate");
491 if ((buf
= malloc(needed
)) == NULL
)
493 if (sysctl(mib
, 6, buf
, &needed
, NULL
, 0) < 0)
494 quit("actual retrieval of interface table");
497 sdl
= NULL
; /* XXX just to keep gcc -Wall happy */
498 for (next
= buf
; next
< lim
; next
+= ifm
->ifm_msglen
) {
499 ifm
= (struct if_msghdr
*)next
;
500 if (ifm
->ifm_type
== RTM_IFINFO
) {
501 sdl
= (struct sockaddr_dl
*)(ifm
+ 1);
502 flags
= ifm
->ifm_flags
;
505 if ((flags
& IFF_UP
) == 0 ||
506 (flags
& (IFF_BROADCAST
|IFF_POINTOPOINT
)) == 0)
508 if (ifm
->ifm_type
!= RTM_NEWADDR
)
509 quit("out of sync parsing NET_RT_IFLIST");
510 ifam
= (struct ifa_msghdr
*)ifm
;
511 info
.rti_addrs
= ifam
->ifam_addrs
;
512 rt_xaddrs((ifam
+ 1), ifam
->ifam_msglen
+ (char *)ifam
, &info
);
513 /* gag, wish we could get rid of Internet dependencies */
514 if (info
.rti_info
[RTAX_BRD
] == NULL
||
515 info
.rti_info
[RTAX_BRD
]->sa_family
!= AF_INET
)
517 (void)memcpy(&dstaddr
, info
.rti_info
[RTAX_BRD
],
519 #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
520 #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
521 PORT_SA(&dstaddr
) = sp
->s_port
;
522 for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
)
523 if (memcmp(sdl
->sdl_data
, np
->n_name
,
524 sdl
->sdl_nlen
) == 0 &&
525 IPADDR_SA(np
->n_addr
) == IPADDR_SA(&dstaddr
))
529 len
= sizeof(*np
) + dstaddr
.sin_len
+ sdl
->sdl_nlen
+ 1;
530 np
= (struct neighbor
*)malloc(len
);
532 quit("malloc of neighbor structure");
533 (void)memset(np
, 0, len
);
535 np
->n_addr
= (struct sockaddr
*)(np
+ 1);
536 np
->n_addrlen
= dstaddr
.sin_len
;
537 np
->n_name
= np
->n_addrlen
+ (char *)np
->n_addr
;
538 np
->n_next
= neighbors
;
540 (void)memcpy(np
->n_addr
, &dstaddr
, np
->n_addrlen
);
541 (void)memcpy(np
->n_name
, sdl
->sdl_data
, sdl
->sdl_nlen
);
549 Sendto(int s
, const void *buf
, size_t cc
, int flags
, const struct sockaddr
*to
,
552 struct whod
*w
= (struct whod
*)buf
;
554 struct sockaddr_in
*sasin
= (struct sockaddr_in
*)to
;
557 ret
= sendto(s
, buf
, cc
, flags
, to
, tolen
);
559 printf("sendto %s.%d\n", inet_ntoa(sasin
->sin_addr
),
560 ntohs(sasin
->sin_port
));
561 printf("hostname %s %s\n", w
->wd_hostname
,
562 interval(ntohl(w
->wd_sendtime
) - ntohl(w
->wd_boottime
), " up"));
563 printf("load %4.2f, %4.2f, %4.2f\n",
564 ntohl(w
->wd_loadav
[0]) / 100.0, ntohl(w
->wd_loadav
[1]) / 100.0,
565 ntohl(w
->wd_loadav
[2]) / 100.0);
567 for (we
= w
->wd_we
, cc
/= sizeof(struct whoent
); cc
> 0; cc
--, we
++) {
568 time_t t
= ntohl(we
->we_utmp
.out_time
);
569 printf("%-8.8s %s:%s %.12s", we
->we_utmp
.out_name
,
570 w
->wd_hostname
, we
->we_utmp
.out_line
, ctime(&t
)+4);
571 we
->we_idle
= ntohl(we
->we_idle
) / 60;
573 if (we
->we_idle
>= 100*60)
574 we
->we_idle
= 100*60 - 1;
575 if (we
->we_idle
>= 60)
576 printf(" %2d", we
->we_idle
/ 60);
579 printf(":%02d", we
->we_idle
% 60);
587 interval(int time
, const char *updown
)
589 static char resbuf
[32];
590 int days
, hours
, minutes
;
592 if (time
< 0 || time
> 3*30*24*60*60) {
593 (void)snprintf(resbuf
, sizeof(resbuf
), " %s ??:??", updown
);
596 minutes
= (time
+ 59) / 60; /* round to minutes */
597 hours
= minutes
/ 60; minutes
%= 60;
598 days
= hours
/ 24; hours
%= 24;
600 (void)snprintf(resbuf
, sizeof(resbuf
), "%s %2d+%02d:%02d",
601 updown
, days
, hours
, minutes
);
603 (void)snprintf(resbuf
, sizeof(resbuf
), "%s %2d:%02d",
604 updown
, hours
, minutes
);
610 drop_privs(char *newuser
)
615 pw
= getpwnam(newuser
);
617 syslog(LOG_ERR
, "no user %.100s", newuser
);
623 gidset
[0] = pw
->pw_gid
;
624 if (setgroups(1, gidset
) == -1) {
625 syslog(LOG_ERR
, "setgroups: %m");
629 if (setgid(pw
->pw_gid
) == -1) {
630 syslog(LOG_ERR
, "setgid: %m");
634 if (setuid(pw
->pw_uid
) == -1) {
635 syslog(LOG_ERR
, "setuid: %m");
645 (void)fprintf(stderr
, "Usage: %s [-i interval] [-u user]\n", getprogname());