#ifndef lint
static const char rcsid[] = "$FreeBSD$";
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <search.h>
#include <rpc/rpc.h>
#include <syslog.h>
#include <vis.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "statd.h"
int
sm_check_hostname(struct svc_req * req, char *arg)
{
int len, dstlen, ret;
struct sockaddr_in *claddr;
char *dst;
len = strlen(arg);
dstlen = (4 * len) + 1;
dst = malloc(dstlen);
claddr = svc_getcaller(req->rq_xprt);
ret = 1;
if (claddr == NULL || dst == NULL) {
ret = 0;
} else if (strvis(dst, arg, VIS_WHITE) != len) {
log(LOG_ERR,
"sm_stat: client %s hostname %s contained invalid characters.",
inet_ntoa(claddr->sin_addr), dst);
ret = 0;
}
free(dst);
return (ret);
}
struct sm_stat_res *
sm_stat_1_svc(sm_name * arg, struct svc_req * req)
{
static sm_stat_res res;
struct addrinfo *ai;
struct sockaddr_in *claddr;
static int err;
err = 1;
if ((err = sm_check_hostname(req, arg->mon_name)) == 0) {
res.res_stat = stat_fail;
}
if (err != 0) {
DEBUG(1, "stat called for host %s", arg->mon_name);
if (getaddrinfo(arg->mon_name, NULL, NULL, &ai) == 0) {
res.res_stat = stat_succ;
freeaddrinfo(ai);
} else {
claddr = svc_getcaller(req->rq_xprt);
log(LOG_ERR, "invalid hostname to sm_stat from %s: %s",
inet_ntoa(claddr->sin_addr), arg->mon_name);
res.res_stat = stat_fail;
}
}
res.state = ntohl(status_info->fh_state);
return (&res);
}
struct sm_stat_res *
sm_mon_1_svc(mon * arg, struct svc_req * req)
{
static sm_stat_res res;
MonitoredHost *mhp;
Notify *np;
struct addrinfo *ai;
int namelen;
res.res_stat = stat_fail;
if (sm_check_hostname(req, arg->mon_id.mon_name) == 0)
return (&res);
DEBUG(1, "monitor request for host %s", arg->mon_id.mon_name);
DEBUG(1, "recall host: %s prog: %d ver: %d proc: %d",
arg->mon_id.my_id.my_name, arg->mon_id.my_id.my_prog,
arg->mon_id.my_id.my_vers, arg->mon_id.my_id.my_proc);
res.state = ntohl(status_info->fh_state);
if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0) {
log(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
return (&res);
}
freeaddrinfo(ai);
namelen = strlen(arg->mon_id.my_id.my_name);
np = malloc(sizeof(Notify) + namelen);
if (!np) {
log(LOG_ERR, "Out of memory");
return (&res);
}
mhp = find_host(arg->mon_id.mon_name, TRUE);
if (!mhp) {
free(np);
return (&res);
}
strncpy(np->n_host, arg->mon_id.my_id.my_name, namelen+1);
np->n_prog = arg->mon_id.my_id.my_prog;
np->n_vers = arg->mon_id.my_id.my_vers;
np->n_proc = arg->mon_id.my_id.my_proc;
memcpy(np->n_data, arg->priv, sizeof(np->n_data));
np->n_next = mhp->mh_notify_list;
mhp->mh_notify_list = np;
res.res_stat = stat_succ;
return (&res);
}
static int
do_unmon(MonitoredHost * mhp, my_id * idp)
{
Notify *np, *next, *last = NULL;
int result = FALSE;
np = mhp->mh_notify_list;
if (!np)
return (result);
while (np) {
if (!strncasecmp(idp->my_name, np->n_host, SM_MAXSTRLEN)
&& (idp->my_prog == np->n_prog) && (idp->my_proc == np->n_proc)
&& (idp->my_vers == np->n_vers)) {
next = np->n_next;
if (last)
last->n_next = next;
else
mhp->mh_notify_list = next;
free(np);
np = next;
result = TRUE;
} else {
last = np;
np = np->n_next;
}
}
if (result && !mhp->mh_notify_list) {
HostInfo *hip = HOSTINFO(mhp->mh_hostinfo_offset);
hip->hi_monitored = 0;
sync_file();
tdelete(mhp, &mhroot, mhcmp);
free(mhp);
}
return (result);
}
struct sm_stat *
sm_unmon_1_svc(mon_id * arg, struct svc_req * req __unused)
{
static sm_stat res;
MonitoredHost *mhp;
DEBUG(1, "un-monitor request for host %s", arg->mon_name);
DEBUG(1, "recall host: %s prog: %d ver: %d proc: %d", arg->my_id.my_name,
arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc);
if ((mhp = find_host(arg->mon_name, FALSE))) {
if (!do_unmon(mhp, &arg->my_id))
log(LOG_ERR, "unmon request from %s, no matching monitor",
arg->my_id.my_name);
} else {
log(LOG_ERR, "unmon request from %s for unknown host %s",
arg->my_id.my_name, arg->mon_name);
}
res.state = ntohl(status_info->fh_state);
return (&res);
}
struct sm_stat *
sm_unmon_all_1_svc(my_id * arg, struct svc_req * req __unused)
{
static sm_stat res;
HostInfo *hip;
MonitoredHost mhtmp, **mhpp;
off_t off;
uint i;
DEBUG(1, "unmon_all for host: %s prog: %d ver: %d proc: %d",
arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
bzero(&mhtmp, sizeof(mhtmp));
off = sizeof(FileHeader);
for (i = 0; i < ntohl(status_info->fh_reccnt); i++) {
hip = HOSTINFO(off);
off += ntohs(hip->hi_len);
if (hip->hi_monitored) {
mhtmp.mh_name = hip->hi_name;
mhpp = tfind(&mhtmp, &mhroot, mhcmp);
if (mhpp)
do_unmon(*mhpp, arg);
}
}
res.state = ntohl(status_info->fh_state);
return (&res);
}
void *
sm_simu_crash_1_svc(void *v __unused, struct svc_req * req)
{
static char dummy;
int need_notify = FALSE;
HostInfo *hip;
off_t off;
uint i;
if (!config.simu_crash_allowed) {
struct sockaddr_in *claddr;
struct hostent *he;
char *hname = NULL;
if ((claddr = svc_getcaller(req->rq_xprt))) {
he = gethostbyaddr((char *) &claddr->sin_addr, sizeof(claddr->sin_addr), AF_INET);
if (he)
hname = he->h_name;
else
hname = inet_ntoa(claddr->sin_addr);
}
log(LOG_WARNING, "simu_crash call from %s denied!", hname);
return (&dummy);
}
log(LOG_WARNING, "simu_crash called!!");
off = sizeof(FileHeader);
for (i = 0; i < ntohl(status_info->fh_reccnt); i++) {
hip = HOSTINFO(off);
if (hip->hi_monitored)
hip->hi_notify = htons(1);
if (hip->hi_notify)
need_notify = TRUE;
off += ntohs(hip->hi_len);
}
status_info->fh_state = htonl(ntohl(status_info->fh_state) + 2);
if (need_notify && !get_statd_notify_pid()) {
log(LOG_INFO, "need to start statd notify");
if (statd_notify_is_loaded())
statd_notify_start();
else
statd_notify_load();
}
return (&dummy);
}
void *
sm_notify_1_svc(stat_chge * arg, struct svc_req * req)
{
struct timeval timeout = {20, 0};
CLIENT *cli;
static char dummy;
char proto[] = "udp", empty[] = "";
sm_status tx_arg;
MonitoredHost *mhp;
Notify *np;
HostInfo *hip;
pid_t pid;
DEBUG(1, "notify from host %s, new state %d", arg->mon_name, arg->state);
mhp = find_host(arg->mon_name, FALSE);
if (!mhp) {
struct sockaddr_in *claddr;
if ((claddr = svc_getcaller(req->rq_xprt))) {
struct hostent *he;
he = gethostbyaddr((char *) &claddr->sin_addr, sizeof(claddr->sin_addr), AF_INET);
if (he) {
char **npp = he->h_aliases;
if (strlen(he->h_name) <= SM_MAXSTRLEN)
mhp = find_host(he->h_name, FALSE);
while (!mhp && *npp) {
if (strlen(*npp) <= SM_MAXSTRLEN)
mhp = find_host(*npp, FALSE);
if (!mhp)
npp++;
}
}
if (mhp)
DEBUG(1, "Notification from host %s found as %s",
arg->mon_name, HOSTINFO(mhp->mh_hostinfo_offset)->hi_name);
}
if (!mhp) {
DEBUG(1, "Unsolicited notification from host %s", arg->mon_name);
return (&dummy);
}
}
hip = HOSTINFO(mhp->mh_hostinfo_offset);
np = mhp->mh_notify_list;
if (!np)
return (&dummy);
pid = fork();
if (pid == -1) {
log(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
return (NULL);
}
if (pid)
return (&dummy);
while (np) {
tx_arg.mon_name = hip->hi_name;
tx_arg.state = arg->state;
memcpy(tx_arg.priv, np->n_data, sizeof(tx_arg.priv));
cli = clnt_create(np->n_host, np->n_prog, np->n_vers, proto);
if (!cli) {
log(LOG_ERR, "Failed to contact host %s%s", np->n_host, clnt_spcreateerror(empty));
} else {
if (clnt_call(cli, np->n_proc, (xdrproc_t) xdr_sm_status, &tx_arg, (xdrproc_t) xdr_void,
&dummy, timeout) != RPC_SUCCESS) {
log(LOG_ERR, "Failed to call rpc.statd client at host %s", np->n_host);
}
clnt_destroy(cli);
}
np = np->n_next;
}
exit(0);
}