4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
35 #pragma ident "%Z%%M% %I% %E% SMI"
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/socket.h>
40 #include <sys/sockio.h>
42 #include <sys/ioctl.h>
44 #include <sys/loadavg.h>
47 #include <netinet/in.h>
57 #include <sys/isa_defs.h> /* for ENDIAN defines */
58 #include <arpa/inet.h>
59 #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 NO_MULTICAST 0 /* multicast modes */
96 #define PER_INTERFACE_MULTICAST 1
97 #define SCOPED_MULTICAST 2
99 #define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */
101 #define INADDR_WHOD_GROUP (ulong_t)0xe0000103 /* 224.0.1.3 */
102 /* (belongs in protocols/rwhod.h) */
104 static int multicast_mode
= NO_MULTICAST
;
105 static int multicast_scope
;
106 static struct sockaddr_in multicast_addr
= { AF_INET
};
110 * Alarm interval. Don't forget to change the down time check in ruptime
111 * if this is changed.
113 #define AL_INTERVAL (3 * 60)
115 static struct sockaddr_in sin
= { AF_INET
};
117 static char myname
[MAXHOSTNAMELEN
];
120 * We communicate with each neighbor in
121 * a list constructed at the time we're
122 * started up. Neighbors are currently
123 * directly connected via a hardware interface.
126 struct neighbor
*n_next
;
127 char *n_name
; /* interface name */
128 char *n_addr
; /* who to send to */
129 int n_addrlen
; /* size of address */
130 ulong_t n_subnet
; /* AF_INET subnet */
131 uint_t n_flags
; /* should forward?, interface flags */
134 static struct neighbor
*neighbors
;
135 static struct whod mywd
;
136 static struct servent
*sp
;
139 #define WHDRSIZE (sizeof (mywd) - sizeof (mywd.wd_we))
140 #define RWHODIR "/var/spool/rwho"
142 static void onalrm(void);
143 static void getkmem(void);
144 static boolean_t
configure(int);
145 static int verify(const struct whod
*);
148 main(int argc
, char *argv
[])
150 struct sockaddr_in from
;
159 (void) fprintf(stderr
, "in.rwhod: not super user\n");
162 sp
= getservbyname("who", "udp");
164 (void) fprintf(stderr
, "in.rwhod: udp/who: unknown service\n");
169 while (argc
> 0 && *argv
[0] == '-') {
170 if (strcmp(*argv
, "-m") == 0) {
171 if (argc
> 1 && isdigit(*(argv
+ 1)[0])) {
174 multicast_mode
= SCOPED_MULTICAST
;
175 multicast_scope
= atoi(*argv
);
176 if (multicast_scope
> MAX_MULTICAST_SCOPE
) {
177 (void) fprintf(stderr
,
179 "ttl must not exceed %u\n",
180 MAX_MULTICAST_SCOPE
);
184 multicast_mode
= PER_INTERFACE_MULTICAST
;
194 if (chdir(RWHODIR
) < 0) {
212 (void) sigset(SIGHUP
, (void (*)())getkmem
);
213 openlog("in.rwhod", LOG_PID
, LOG_DAEMON
);
215 * Establish host name as returned by system.
217 if (gethostname(myname
, sizeof (myname
) - 1) < 0) {
218 syslog(LOG_ERR
, "main: gethostname: %m");
221 if ((cp
= index(myname
, '.')) != NULL
)
223 (void) strlcpy(mywd
.wd_hostname
, myname
, sizeof (mywd
.wd_hostname
));
225 if (stat(UTMPX_FILE
, &sb
) < 0) {
226 syslog(LOG_ERR
, "main: stat: %s: %m", UTMPX_FILE
);
230 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
231 syslog(LOG_ERR
, "main: socket: %m");
234 if (setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &on
, sizeof (on
)) < 0) {
235 syslog(LOG_ERR
, "main: setsockopt SO_BROADCAST: %m");
238 hp
= gethostbyname(myname
);
240 syslog(LOG_ERR
, "main: %s: don't know my own name\n", myname
);
243 sin
.sin_family
= hp
->h_addrtype
;
244 sin
.sin_port
= sp
->s_port
;
245 if (bind(s
, (struct sockaddr
*)&sin
, sizeof (sin
)) < 0) {
246 syslog(LOG_ERR
, "main: bind: %m");
251 (void) sigset(SIGALRM
, (void (*)())onalrm
);
256 socklen_t len
= sizeof (from
);
258 cc
= recvfrom(s
, &wd
, sizeof (struct whod
), 0,
259 (struct sockaddr
*)&from
, &len
);
261 if (cc
< 0 && errno
!= EINTR
)
262 syslog(LOG_WARNING
, "main: recvfrom: %m");
265 if (from
.sin_port
!= sp
->s_port
) {
266 syslog(LOG_WARNING
, "main: %d: bad from port",
267 ntohs(from
.sin_port
));
271 if (gethostbyname(wd
.wd_hostname
) == 0) {
272 syslog(LOG_WARNING
, "main: %s: unknown host",
277 if (wd
.wd_vers
!= WHODVERSION
)
279 if (wd
.wd_type
!= WHODTYPE_STATUS
)
282 syslog(LOG_WARNING
, "main: malformed host name from %x",
283 from
.sin_addr
.s_addr
);
286 (void) sprintf(path
, "whod.%s", wd
.wd_hostname
);
288 * Rather than truncating and growing the file each time,
289 * use ftruncate if size is less than previous size.
291 whod
= open(path
, O_WRONLY
| O_CREAT
, 0644);
293 syslog(LOG_WARNING
, "main: open: %s: %m", path
);
296 #if defined(_LITTLE_ENDIAN)
299 int i
, n
= (cc
- WHDRSIZE
)/sizeof (struct whoent
);
302 /* undo header byte swapping before writing to file */
303 wd
.wd_sendtime
= ntohl(wd
.wd_sendtime
);
304 for (i
= 0; i
< 3; i
++)
305 wd
.wd_loadav
[i
] = ntohl(wd
.wd_loadav
[i
]);
306 wd
.wd_boottime
= ntohl(wd
.wd_boottime
);
308 for (i
= 0; i
< n
; i
++) {
309 we
->we_idle
= ntohl(we
->we_idle
);
310 we
->we_utmp
.out_time
=
311 ntohl(we
->we_utmp
.out_time
);
316 (void) time((time_t *)&wd
.wd_recvtime
);
317 (void) write(whod
, &wd
, cc
);
318 if (fstat(whod
, &st
) < 0 || st
.st_size
> cc
)
319 (void) ftruncate(whod
, cc
);
324 (void) fprintf(stderr
, "usage: in.rwhod [ -m [ ttl ] ]\n");
329 * Check out host name for unprintables
330 * and other funnies before allowing a file
331 * to be created. Sorry, but blanks aren't allowed.
334 verify(const struct whod
*wd
)
337 const char *name
= wd
->wd_hostname
;
340 * We shouldn't assume the name is NUL terminated, so bound the
341 * checks at the size of the whod structures wd_hostname field.
343 while ((size
< sizeof (wd
->wd_hostname
)) &&
345 if (*name
== '/' || !isascii(*name
) ||
346 !(isalnum(*name
) || ispunct(*name
)))
351 * Fail the verification if NULL name or it wasn't NUL terminated.
353 return ((size
> 0) && (size
< sizeof (wd
->wd_hostname
)));
356 static int utmpxtime
;
358 static int alarmcount
;
369 struct utmpx
*utmpxbegin
;
370 struct whoent
*we
= mywd
.wd_we
, *wlast
;
374 time_t now
= time(0);
377 if (alarmcount
% 10 == 0)
380 (void) stat(UTMPX_FILE
, &stb
);
381 entries
= stb
.st_size
/ sizeof (struct futmpx
);
382 if ((stb
.st_mtime
!= utmpxtime
) || (entries
> utmpxent
)) {
383 utmpxtime
= stb
.st_mtime
;
384 if (entries
> utmpxent
) {
386 utmpxsize
= utmpxent
* sizeof (struct utmpx
);
387 utmpx
= realloc(utmpx
, utmpxsize
);
389 syslog(LOG_ERR
, "onalrm: realloc: %m");
397 while (cnt
++ < utmpxent
&& (utp
= getutxent()) != NULL
)
398 (void) memcpy(utmpxbegin
++, utp
, sizeof (struct utmpx
));
400 wlast
= &mywd
.wd_we
[1024 / sizeof (struct whoent
) - 1];
401 for (i
= 0; i
< utmpxent
; i
++) {
402 if (utmpx
[i
].ut_name
[0] &&
403 utmpx
[i
].ut_type
== USER_PROCESS
) {
405 * XXX - utmpx name and line lengths should
408 bcopy(utmpx
[i
].ut_line
, we
->we_utmp
.out_line
,
409 sizeof (we
->we_utmp
.out_line
));
410 bcopy(utmpx
[i
].ut_name
, we
->we_utmp
.out_name
,
411 sizeof (we
->we_utmp
.out_name
));
412 we
->we_utmp
.out_time
=
413 htonl(utmpx
[i
].ut_xtime
);
419 utmpxent
= we
- mywd
.wd_we
;
423 * The test on utmpxent looks silly---after all, if no one is
424 * logged on, why worry about efficiency?---but is useful on
425 * (e.g.) compute servers.
427 if (utmpxent
> 0 && chdir("/dev") == -1) {
428 syslog(LOG_ERR
, "onalrm: chdir /dev: %m");
432 for (i
= 0; i
< utmpxent
; i
++) {
433 if (stat(we
->we_utmp
.out_line
, &stb
) >= 0)
434 we
->we_idle
= htonl(now
- stb
.st_atime
);
437 if (getloadavg(avenrun
, 3) == -1) {
438 syslog(LOG_ERR
, "onalrm: getloadavg: %m");
442 for (i
= 0; i
< 3; i
++)
443 mywd
.wd_loadav
[i
] = htonl((ulong_t
)(avenrun
[i
] * 100));
444 cc
= (char *)we
- (char *)&mywd
;
445 mywd
.wd_sendtime
= htonl(time(0));
446 mywd
.wd_vers
= WHODVERSION
;
447 mywd
.wd_type
= WHODTYPE_STATUS
;
448 if (multicast_mode
== SCOPED_MULTICAST
) {
449 (void) sendto(s
, &mywd
, cc
, 0,
450 (struct sockaddr
*)&multicast_addr
,
451 sizeof (multicast_addr
));
452 } else for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
) {
453 if (multicast_mode
== PER_INTERFACE_MULTICAST
&&
454 np
->n_flags
& IFF_MULTICAST
) {
456 * Select the outgoing interface for the multicast.
458 if (setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
,
459 &(((struct sockaddr_in
*)np
->n_addr
)->sin_addr
),
460 sizeof (struct in_addr
)) < 0) {
462 "onalrm: setsockopt IP_MULTICAST_IF: %m");
465 (void) sendto(s
, &mywd
, cc
, 0,
466 (struct sockaddr
*)&multicast_addr
,
467 sizeof (multicast_addr
));
469 (void) sendto(s
, &mywd
, cc
, 0,
470 (struct sockaddr
*)np
->n_addr
, np
->n_addrlen
);
473 if (utmpxent
> 0 && chdir(RWHODIR
) == -1) {
474 syslog(LOG_ERR
, "onalrm: chdir %s: %m", RWHODIR
);
478 (void) alarm(AL_INTERVAL
);
484 struct utmpx
*utmpx
, utmpx_id
;
486 utmpx_id
.ut_type
= BOOT_TIME
;
487 if ((utmpx
= getutxid(&utmpx_id
)) != NULL
)
488 mywd
.wd_boottime
= utmpx
->ut_xtime
;
490 mywd
.wd_boottime
= htonl(mywd
.wd_boottime
);
494 * Figure out device configuration and select
495 * networks which deserve status information.
502 struct ifreq ifreq
, *ifr
;
503 struct sockaddr_in
*sin
;
505 struct neighbor
*np2
;
510 if (multicast_mode
== SCOPED_MULTICAST
) {
514 mreq
.imr_multiaddr
.s_addr
= htonl(INADDR_WHOD_GROUP
);
515 mreq
.imr_interface
.s_addr
= htonl(INADDR_ANY
);
516 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
,
517 sizeof (mreq
)) < 0) {
519 "configure: setsockopt IP_ADD_MEMBERSHIP: %m");
522 ttl
= multicast_scope
;
523 if (setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
,
526 "configure: setsockopt IP_MULTICAST_TTL: %m");
529 multicast_addr
.sin_addr
.s_addr
= htonl(INADDR_WHOD_GROUP
);
530 multicast_addr
.sin_port
= sp
->s_port
;
534 if (ioctl(s
, SIOCGIFNUM
, (char *)&numifs
) < 0) {
535 syslog(LOG_ERR
, "configure: ioctl SIOCGIFNUM: %m");
538 bufsize
= numifs
* sizeof (struct ifreq
);
539 buf
= malloc(bufsize
);
541 syslog(LOG_ERR
, "configure: malloc: %m");
544 ifc
.ifc_len
= bufsize
;
546 if (ioctl(s
, SIOCGIFCONF
, (char *)&ifc
) < 0) {
548 "configure: ioctl (get interface configuration): %m");
553 for (n
= ifc
.ifc_len
/ sizeof (struct ifreq
); n
> 0; n
--, ifr
++) {
554 /* Skip all logical interfaces */
555 if (index(ifr
->ifr_name
, ':') != NULL
)
558 for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
) {
560 strcmp(ifr
->ifr_name
, np
->n_name
) == 0)
566 np
= (struct neighbor
*)malloc(sizeof (*np
));
569 np
->n_name
= malloc(strlen(ifr
->ifr_name
) + 1);
570 if (np
->n_name
== NULL
) {
574 (void) strcpy(np
->n_name
, ifr
->ifr_name
);
575 np
->n_addrlen
= sizeof (ifr
->ifr_addr
);
576 np
->n_addr
= malloc(np
->n_addrlen
);
577 if (np
->n_addr
== NULL
) {
582 bcopy(&ifr
->ifr_addr
, np
->n_addr
, np
->n_addrlen
);
583 if (ioctl(s
, SIOCGIFFLAGS
, (char *)&ifreq
) < 0) {
585 "configure: ioctl (get interface flags): %m");
591 np
->n_flags
= ifreq
.ifr_flags
;
592 if (((struct sockaddr_in
*)np
->n_addr
)->sin_family
== AF_INET
&&
593 ioctl(s
, SIOCGIFNETMASK
, (char *)&ifreq
) >= 0) {
594 sin
= (struct sockaddr_in
*)np
->n_addr
;
596 np
->n_subnet
= sin
->sin_addr
.s_addr
&
597 ((struct sockaddr_in
*)&ifreq
.ifr_addr
)->
600 if (multicast_mode
== PER_INTERFACE_MULTICAST
&&
601 (np
->n_flags
& IFF_UP
) &&
602 (np
->n_flags
& IFF_MULTICAST
) &&
603 !(np
->n_flags
& IFF_LOOPBACK
)) {
607 * Skip interfaces that have matching subnets i.e.
608 * (addr & netmask) are identical.
609 * Such interfaces are connected to the same
612 for (np2
= neighbors
; np2
!= NULL
; np2
= np2
->n_next
) {
614 if (!(np
->n_flags
& IFF_POINTOPOINT
) &&
615 !(np2
->n_flags
& IFF_POINTOPOINT
) &&
616 (np
->n_subnet
== np2
->n_subnet
)) {
626 mreq
.imr_multiaddr
.s_addr
= htonl(INADDR_WHOD_GROUP
);
627 mreq
.imr_interface
.s_addr
=
628 ((struct sockaddr_in
*)np
->n_addr
)->sin_addr
.s_addr
;
629 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
,
630 sizeof (mreq
)) < 0) {
633 "setsockopt IP_ADD_MEMBERSHIP: %m");
639 multicast_addr
.sin_addr
.s_addr
=
640 htonl(INADDR_WHOD_GROUP
);
641 multicast_addr
.sin_port
= sp
->s_port
;
642 np
->n_next
= neighbors
;
646 if ((np
->n_flags
& IFF_UP
) == 0 ||
647 (np
->n_flags
& (IFF_BROADCAST
|IFF_POINTOPOINT
)) == 0) {
653 if (np
->n_flags
& IFF_POINTOPOINT
) {
654 if (ioctl(s
, SIOCGIFDSTADDR
, (char *)&ifreq
) < 0) {
656 "configure: ioctl (get dstaddr): %m");
662 /* we assume addresses are all the same size */
663 bcopy(&ifreq
.ifr_dstaddr
, np
->n_addr
, np
->n_addrlen
);
665 if (np
->n_flags
& IFF_BROADCAST
) {
666 if (ioctl(s
, SIOCGIFBRDADDR
, (char *)&ifreq
) < 0) {
668 "configure: ioctl (get broadaddr): %m");
674 /* we assume addresses are all the same size */
675 bcopy(&ifreq
.ifr_broadaddr
, np
->n_addr
, np
->n_addrlen
);
677 /* gag, wish we could get rid of Internet dependencies */
678 sin
= (struct sockaddr_in
*)np
->n_addr
;
679 sin
->sin_port
= sp
->s_port
;
682 * Avoid adding duplicate broadcast and pt-pt destinations
685 for (np2
= neighbors
; np2
!= NULL
; np2
= np2
->n_next
) {
686 struct sockaddr_in
*sin2
;
688 sin2
= (struct sockaddr_in
*)np2
->n_addr
;
689 if (sin2
->sin_addr
.s_addr
== sin
->sin_addr
.s_addr
) {
699 np
->n_next
= neighbors
;
707 static char *interval(uint_t
, char *);
711 sendto(int s
, const void *buf
, size_t cc
, int flags
, const struct sockaddr
*to
,
714 struct whod
*w
= (struct whod
*)buf
;
716 struct sockaddr_in
*sin
= (struct sockaddr_in
*)to
;
719 (void) printf("sendto %x.%d\n", ntohl(sin
->sin_addr
.s_addr
),
720 ntohs(sin
->sin_port
));
721 (void) printf("hostname %s %s\n", w
->wd_hostname
,
722 interval(ntohl(w
->wd_sendtime
) - ntohl(w
->wd_boottime
), " up"));
723 (void) printf("load %4.2f, %4.2f, %4.2f\n",
724 ntohl(w
->wd_loadav
[0]) / 100.0, ntohl(w
->wd_loadav
[1]) / 100.0,
725 ntohl(w
->wd_loadav
[2]) / 100.0);
727 for (we
= w
->wd_we
, cc
/= sizeof (struct whoent
); cc
> 0; cc
--, we
++) {
728 time_t t
= ntohl(we
->we_utmp
.out_time
);
730 nsz
= sizeof (we
->we_utmp
.out_name
);
731 (void) printf("%-*.*s %s:%s %.12s",
734 we
->we_utmp
.out_name
,
736 we
->we_utmp
.out_line
,
738 we
->we_idle
= ntohl(we
->we_idle
) / 60;
740 if (we
->we_idle
>= 100*60)
741 we
->we_idle
= 100*60 - 1;
742 if (we
->we_idle
>= 60)
743 (void) printf(" %2d", we
->we_idle
/ 60);
746 (void) printf(":%02d", we
->we_idle
% 60);
754 interval(uint_t time
, char *updown
)
756 static char resbuf
[32];
757 int days
, hours
, minutes
;
759 if (time
> 3*30*24*60*60) {
760 (void) sprintf(resbuf
, " %s ??:??", updown
);
763 minutes
= (time
+ 59) / 60; /* round to minutes */
764 hours
= minutes
/ 60;
769 (void) sprintf(resbuf
, "%s %2d+%02d:%02d",
770 updown
, days
, hours
, minutes
);
772 (void) sprintf(resbuf
, "%s %2d:%02d",
773 updown
, hours
, minutes
);