#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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)
{
syslog(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)
{
if (debug)
syslog(LOG_DEBUG, "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);
syslog(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 = status_info->ourState;
return (&res);
}
struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
{
static sm_stat_res res;
HostInfo *hp;
static int err;
MonList *lp;
struct addrinfo *ai;
if ((err = sm_check_hostname(req, arg->mon_id.mon_name)) == 0)
{
res.res_stat = stat_fail;
}
if (err != 0)
{
if (debug)
{
syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
arg->mon_id.mon_name,
arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers,
arg->mon_id.my_id.my_proc);
}
res.res_stat = stat_fail;
res.state = status_info->ourState;
if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0)
{
syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
return (&res);
}
freeaddrinfo(ai);
if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
{
lp = (MonList *)malloc(sizeof(MonList));
if (!lp)
{
syslog(LOG_ERR, "Out of memory");
}
else
{
strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
lp->notifyProg = arg->mon_id.my_id.my_prog;
lp->notifyVers = arg->mon_id.my_id.my_vers;
lp->notifyProc = arg->mon_id.my_id.my_proc;
memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
lp->next = hp->monList;
hp->monList = lp;
sync_file();
res.res_stat = stat_succ;
}
}
}
return (&res);
}
static int do_unmon(HostInfo *hp, my_id *idp)
{
MonList *lp, *next;
MonList *last = NULL;
int result = FALSE;
lp = hp->monList;
while (lp)
{
if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
&& (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
&& (idp->my_vers == lp->notifyVers))
{
next = lp->next;
if (last) last->next = next;
else hp->monList = next;
free(lp);
lp = next;
result = TRUE;
}
else
{
last = lp;
lp = lp->next;
}
}
return (result);
}
struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req __unused)
{
static sm_stat res;
HostInfo *hp;
if (debug)
{
syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
arg->mon_name,
arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc);
}
if ((hp = find_host(arg->mon_name, FALSE)))
{
if (do_unmon(hp, &arg->my_id)) sync_file();
else
{
syslog(LOG_ERR, "unmon request from %s, no matching monitor",
arg->my_id.my_name);
}
}
else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
arg->my_id.my_name, arg->mon_name);
res.state = status_info->ourState;
return (&res);
}
struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req __unused)
{
static sm_stat res;
HostInfo *hp;
int i;
if (debug)
{
syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
}
for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
{
do_unmon(hp, arg);
}
sync_file();
res.state = status_info->ourState;
return (&res);
}
void *sm_simu_crash_1_svc(void *v __unused, struct svc_req *req __unused)
{
static char dummy;
int work_to_do = FALSE;
HostInfo *hp;
int i;
if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
{
if (hp->monList)
{
work_to_do = TRUE;
hp->notifyReqd = TRUE;
}
}
status_info->ourState += 2;
if (work_to_do) notify_hosts();
return (&dummy);
}
void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req __unused)
{
struct timeval timeout = { 20, 0 };
CLIENT *cli;
static char dummy;
sm_status tx_arg;
MonList *lp;
HostInfo *hp;
pid_t pid;
if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
arg->mon_name, arg->state);
hp = find_host(arg->mon_name, FALSE);
if (!hp)
{
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 **np = he->h_aliases;
hp = find_host(he->h_name, FALSE);
while (!hp && *np) {
hp = find_host(*np, FALSE);
if (!hp)
np++;
}
}
if (hp)
syslog(LOG_DEBUG, "Notification from host %s found as %s",
arg->mon_name, hp->hostname);
}
if (!hp) {
syslog(LOG_DEBUG, "Unsolicited notification from host %s", arg->mon_name);
return (&dummy);
}
}
lp = hp->monList;
if (!lp) return (&dummy);
pid = fork();
if (pid == -1)
{
syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
return (NULL);
}
if (pid) return (&dummy);
while (lp)
{
tx_arg.mon_name = hp->hostname;
tx_arg.state = arg->state;
memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
if (!cli)
{
syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
clnt_spcreateerror(""));
}
else
{
if (clnt_call(cli, lp->notifyProc, xdr_sm_status, &tx_arg, xdr_void,
&dummy, timeout) != RPC_SUCCESS)
{
syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
lp->notifyHost);
}
clnt_destroy(cli);
}
lp = lp->next;
}
exit (0);
}