* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
static char sccsid
[] = "@(#)rwhod.c 5.3 (Berkeley) %G%";
#include <protocols/rwhod.h>
* Alarm interval. Don't forget to change the down time check in ruptime
#define AL_INTERVAL (3 * 60)
struct sockaddr_in sin
= { AF_INET
};
* We communicate with each neighbor in
* a list constructed at the time we're
* started up. Neighbors are currently
* directly connected via a hardware interface.
char *n_name
; /* interface name */
char *n_addr
; /* who to send to */
int n_addrlen
; /* size of address */
int n_flags
; /* should forward?, interface flags */
struct neighbor
*neighbors
;
int s
, utmpf
, kmemf
= -1;
#define WHDRSIZE (sizeof (mywd) - sizeof (mywd.wd_we))
#define RWHODIR "/usr/spool/rwho"
char *strcpy(), *sprintf(), *malloc();
struct in_addr
inet_makeaddr();
fprintf(stderr
, "rwhod: not super user\n");
sp
= getservbyname("who", "udp");
fprintf(stderr
, "rwhod: udp/who: unknown service\n");
(void) signal(SIGHUP
, getkmem
);
openlog("rwhod", LOG_PID
, 0);
* Establish host name as returned by system.
if (gethostname(myname
, sizeof (myname
) - 1) < 0) {
syslog(LOG_ERR
, "gethostname: %m");
strncpy(mywd
.wd_hostname
, myname
, sizeof (myname
) - 1);
utmpf
= open("/etc/utmp", O_RDONLY
);
(void) close(creat("/etc/utmp", 0644));
utmpf
= open("/etc/utmp", O_RDONLY
);
syslog(LOG_ERR
, "/etc/utmp: %m");
if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
syslog(LOG_ERR
, "socket: %m");
if (setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &on
, sizeof (on
)) < 0) {
syslog(LOG_ERR
, "setsockopt SO_BROADCAST: %m");
hp
= gethostbyname(myname
);
syslog(LOG_ERR
, "%s: don't know my own name", myname
);
sin
.sin_family
= hp
->h_addrtype
;
sin
.sin_port
= sp
->s_port
;
if (bind(s
, &sin
, sizeof (sin
)) < 0) {
syslog(LOG_ERR
, "bind: %m");
int cc
, whod
, len
= sizeof (from
);
cc
= recvfrom(s
, (char *)&wd
, sizeof (struct whod
), 0,
if (cc
< 0 && errno
!= EINTR
)
syslog(LOG_WARNING
, "recv: %m");
if (from
.sin_port
!= sp
->s_port
) {
syslog(LOG_WARNING
, "%d: bad from port",
if (gethostbyname(wd
.wd_hostname
) == 0) {
syslog(LOG_WARNING
, "%s: unknown host",
if (wd
.wd_vers
!= WHODVERSION
)
if (wd
.wd_type
!= WHODTYPE_STATUS
)
if (!verify(wd
.wd_hostname
)) {
syslog(LOG_WARNING
, "malformed host name from %x",
(void) sprintf(path
, "%s/whod.%s", RWHODIR
, wd
.wd_hostname
);
whod
= creat(path
, 0666);
syslog(LOG_WARNING
, "%s: %m", path
);
int i
, n
= (cc
- WHDRSIZE
)/sizeof(struct whoent
);
/* undo header byte swapping before writing to file */
wd
.wd_sendtime
= ntohl(wd
.wd_sendtime
);
wd
.wd_loadav
[i
] = ntohl(wd
.wd_loadav
[i
]);
wd
.wd_boottime
= ntohl(wd
.wd_boottime
);
for (i
= 0; i
< n
; i
++) {
we
->we_idle
= ntohl(we
->we_idle
);
ntohl(we
->we_utmp
.out_time
);
(void) time(&wd
.wd_recvtime
);
(void) write(whod
, (char *)&wd
, cc
);
* Check out host name for unprintables
* and other funnies before allowing a file
* to be created. Sorry, but blanks aren't allowed.
if (!isascii(*name
) || !(isalnum(*name
) || ispunct(*name
)))
register struct whoent
*we
= mywd
.wd_we
, *wlast
;
register struct neighbor
*np
;
if (alarmcount
% 10 == 0)
(void) fstat(utmpf
, &stb
);
if ((stb
.st_mtime
!= utmptime
) || (stb
.st_size
> utmpsize
)) {
if (stb
.st_size
> utmpsize
) {
utmpsize
= stb
.st_size
+ 10 * sizeof(struct utmp
);
utmp
= (struct utmp
*)realloc(utmp
, utmpsize
);
utmp
= (struct utmp
*)malloc(utmpsize
);
fprintf(stderr
, "rwhod: malloc failed\n");
(void) lseek(utmpf
, (long)0, L_SET
);
cc
= read(utmpf
, (char *)utmp
, stb
.st_size
);
wlast
= &mywd
.wd_we
[1024 / sizeof (struct whoent
) - 1];
utmpent
= cc
/ sizeof (struct utmp
);
for (i
= 0; i
< utmpent
; i
++)
if (utmp
[i
].ut_name
[0]) {
bcopy(utmp
[i
].ut_line
, we
->we_utmp
.out_line
,
sizeof (utmp
[i
].ut_line
));
bcopy(utmp
[i
].ut_name
, we
->we_utmp
.out_name
,
sizeof (utmp
[i
].ut_name
));
we
->we_utmp
.out_time
= htonl(utmp
[i
].ut_time
);
utmpent
= we
- mywd
.wd_we
;
for (i
= 0; i
< utmpent
; i
++) {
if (stat(we
->we_utmp
.out_line
, &stb
) >= 0)
we
->we_idle
= htonl(now
- stb
.st_atime
);
(void) lseek(kmemf
, (long)nl
[NL_AVENRUN
].n_value
, L_SET
);
(void) read(kmemf
, (char *)avenrun
, sizeof (avenrun
));
mywd
.wd_loadav
[i
] = htonl((u_long
)(avenrun
[i
] * 100));
cc
= (char *)we
- (char *)&mywd
;
mywd
.wd_sendtime
= htonl(time(0));
mywd
.wd_vers
= WHODVERSION
;
mywd
.wd_type
= WHODTYPE_STATUS
;
for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
)
(void) sendto(s
, (char *)&mywd
, cc
, 0,
np
->n_addr
, np
->n_addrlen
);
(void) alarm(AL_INTERVAL
);
static time_t vmunixctime
;
if (stat("/vmunix", &sb
) < 0) {
if (sb
.st_ctime
== vmunixctime
&& sb
.st_ino
== vmunixino
)
vmunixctime
= sb
.st_ctime
;
if (nlist("/vmunix", nl
)) {
syslog(LOG_WARNING
, "/vmunix namelist botch");
kmemf
= open("/dev/kmem", O_RDONLY
);
syslog(LOG_ERR
, "/dev/kmem: %m");
(void) lseek(kmemf
, (long)nl
[NL_BOOTTIME
].n_value
, L_SET
);
(void) read(kmemf
, (char *)&mywd
.wd_boottime
,
sizeof (mywd
.wd_boottime
));
mywd
.wd_boottime
= htonl(mywd
.wd_boottime
);
* Figure out device configuration and select
* networks which deserve status information.
struct ifreq ifreq
, *ifr
;
register struct neighbor
*np
;
ifc
.ifc_len
= sizeof (buf
);
if (ioctl(s
, SIOCGIFCONF
, (char *)&ifc
) < 0) {
syslog(LOG_ERR
, "ioctl (get interface configuration)");
for (n
= ifc
.ifc_len
/ sizeof (struct ifreq
); n
> 0; n
--, ifr
++) {
for (np
= neighbors
; np
!= NULL
; np
= np
->n_next
)
strcmp(ifr
->ifr_name
, np
->n_name
) == 0)
np
= (struct neighbor
*)malloc(sizeof (*np
));
np
->n_name
= malloc(strlen(ifr
->ifr_name
) + 1);
if (np
->n_name
== NULL
) {
strcpy(np
->n_name
, ifr
->ifr_name
);
np
->n_addrlen
= sizeof (ifr
->ifr_addr
);
np
->n_addr
= malloc(np
->n_addrlen
);
if (np
->n_addr
== NULL
) {
bcopy((char *)&ifr
->ifr_addr
, np
->n_addr
, np
->n_addrlen
);
if (ioctl(s
, SIOCGIFFLAGS
, (char *)&ifreq
) < 0) {
syslog(LOG_ERR
, "ioctl (get interface flags)");
if ((ifreq
.ifr_flags
& IFF_UP
) == 0 ||
(ifreq
.ifr_flags
& (IFF_BROADCAST
|IFF_POINTOPOINT
)) == 0) {
np
->n_flags
= ifreq
.ifr_flags
;
if (np
->n_flags
& IFF_POINTOPOINT
) {
if (ioctl(s
, SIOCGIFDSTADDR
, (char *)&ifreq
) < 0) {
syslog(LOG_ERR
, "ioctl (get dstaddr)");
/* we assume addresses are all the same size */
bcopy((char *)&ifreq
.ifr_dstaddr
,
np
->n_addr
, np
->n_addrlen
);
if (np
->n_flags
& IFF_BROADCAST
) {
if (ioctl(s
, SIOCGIFBRDADDR
, (char *)&ifreq
) < 0) {
syslog(LOG_ERR
, "ioctl (get broadaddr)");
/* we assume addresses are all the same size */
bcopy((char *)&ifreq
.ifr_broadaddr
,
np
->n_addr
, np
->n_addrlen
);
/* gag, wish we could get rid of Internet dependencies */
sin
= (struct sockaddr_in
*)np
->n_addr
;
sin
->sin_port
= sp
->s_port
;
sendto(s
, buf
, cc
, flags
, to
, tolen
)
register struct whod
*w
= (struct whod
*)buf
;
register struct whoent
*we
;
struct sockaddr_in
*sin
= (struct sockaddr_in
*)to
;
printf("sendto %x.%d\n", ntohl(sin
->sin_addr
), ntohs(sin
->sin_port
));
printf("hostname %s %s\n", w
->wd_hostname
,
interval(ntohl(w
->wd_sendtime
) - ntohl(w
->wd_boottime
), " up"));
printf("load %4.2f, %4.2f, %4.2f\n",
ntohl(w
->wd_loadav
[0]) / 100.0, ntohl(w
->wd_loadav
[1]) / 100.0,
ntohl(w
->wd_loadav
[2]) / 100.0);
for (we
= w
->wd_we
, cc
/= sizeof (struct whoent
); cc
> 0; cc
--, we
++) {
time_t t
= ntohl(we
->we_utmp
.out_time
);
printf("%-8.8s %s:%s %.12s",
w
->wd_hostname
, we
->we_utmp
.out_line
,
we
->we_idle
= ntohl(we
->we_idle
) / 60;
if (we
->we_idle
>= 100*60)
we
->we_idle
= 100*60 - 1;
printf(" %2d", we
->we_idle
/ 60);
printf(":%02d", we
->we_idle
% 60);
int days
, hours
, minutes
;
if (time
< 0 || time
> 3*30*24*60*60) {
(void) sprintf(resbuf
, " %s ??:??", updown
);
minutes
= (time
+ 59) / 60; /* round to minutes */
hours
= minutes
/ 60; minutes
%= 60;
days
= hours
/ 24; hours
%= 24;
(void) sprintf(resbuf
, "%s %2d+%02d:%02d",
updown
, days
, hours
, minutes
);
(void) sprintf(resbuf
, "%s %2d:%02d",