114125Speter/* 214125Speter * Copyright (c) 1995 314125Speter * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved. 414125Speter * 514125Speter * Redistribution and use in source and binary forms, with or without 614125Speter * modification, are permitted provided that the following conditions 714125Speter * are met: 814125Speter * 1. Redistributions of source code must retain the above copyright 914125Speter * notice, this list of conditions and the following disclaimer. 1014125Speter * 2. Redistributions in binary form must reproduce the above copyright 1114125Speter * notice, this list of conditions and the following disclaimer in the 1214125Speter * documentation and/or other materials provided with the distribution. 1314125Speter * 3. All advertising materials mentioning features or use of this software 1414125Speter * must display the following acknowledgement: 1514125Speter * This product includes software developed for the FreeBSD project 1614125Speter * 4. Neither the name of the author nor the names of any co-contributors 1714125Speter * may be used to endorse or promote products derived from this software 1814125Speter * without specific prior written permission. 1914125Speter * 2014125Speter * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND 2114125Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2214125Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2314125Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2414125Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2514125Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2614125Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2714125Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2814125Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2914125Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3014125Speter * SUCH DAMAGE. 3114125Speter * 3214125Speter */ 3314125Speter 34162263Scharnier#include <sys/cdefs.h> 35162263Scharnier__FBSDID("$FreeBSD$"); 3614125Speter 3730376Scharnier#include <errno.h> 3814125Speter#include <stdio.h> 3914125Speter#include <stdlib.h> 4014125Speter#include <string.h> 4130376Scharnier#include <unistd.h> 4214125Speter#include <rpc/rpc.h> 4314125Speter#include <syslog.h> 4499783Salfred#include <vis.h> 45100126Salfred#include <netdb.h> /* for getaddrinfo() */ 4699789Salfred#include <sys/types.h> 4799789Salfred#include <sys/socket.h> 4899789Salfred#include <netinet/in.h> 4999789Salfred#include <arpa/inet.h> 5014125Speter 5114125Speter#include "statd.h" 5214125Speter 53168276Smatteostatic const char * 54168276Smatteofrom_addr(saddr) 55168276Smatteo struct sockaddr *saddr; 56168276Smatteo{ 57168276Smatteo static char inet_buf[INET6_ADDRSTRLEN]; 58168276Smatteo 59168276Smatteo if (getnameinfo(saddr, saddr->sa_len, inet_buf, sizeof(inet_buf), 60168276Smatteo NULL, 0, NI_NUMERICHOST) == 0) 61168276Smatteo return inet_buf; 62168276Smatteo return "???"; 63168276Smatteo} 64168276Smatteo 6599783Salfred/* sm_check_hostname -------------------------------------------------------- */ 6614125Speter/* 6799783Salfred * Purpose: Check `mon_name' member of sm_name struct to ensure that the array 6899783Salfred * consists only of printable characters. 6999783Salfred * 7099783Salfred * Returns: TRUE if hostname is good. FALSE if hostname contains binary or 7199783Salfred * otherwise non-printable characters. 7299783Salfred * 7399783Salfred * Notes: Will syslog(3) to warn of corrupt hostname. 7499783Salfred */ 7599783Salfred 7699783Salfredint sm_check_hostname(struct svc_req *req, char *arg) 7799783Salfred{ 7899783Salfred int len, dstlen, ret; 79168276Smatteo struct sockaddr *claddr; 8099783Salfred char *dst; 8199783Salfred 8299783Salfred len = strlen(arg); 8399783Salfred dstlen = (4 * len) + 1; 8499783Salfred dst = malloc(dstlen); 85168276Smatteo claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ; 8699783Salfred ret = 1; 8799783Salfred 8899783Salfred if (claddr == NULL || dst == NULL) 8999783Salfred { 9099783Salfred ret = 0; 9199783Salfred } 9299783Salfred else if (strvis(dst, arg, VIS_WHITE) != len) 9399783Salfred { 9499783Salfred syslog(LOG_ERR, 9599783Salfred "sm_stat: client %s hostname %s contained invalid characters.", 96168276Smatteo from_addr(claddr), 9799783Salfred dst); 9899783Salfred ret = 0; 9999783Salfred } 10099783Salfred free(dst); 10199783Salfred return (ret); 10299783Salfred} 10399783Salfred 10499783Salfred/* sm_stat_1 --------------------------------------------------------------- */ 10599783Salfred/* 10614125Speter Purpose: RPC call to enquire if a host can be monitored 10714125Speter Returns: TRUE for any hostname that can be looked up to give 10814125Speter an address. 10914125Speter*/ 11014125Speter 11114982Speterstruct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req) 11214125Speter{ 11314125Speter static sm_stat_res res; 114100126Salfred struct addrinfo *ai; 115168276Smatteo struct sockaddr *claddr; 11699783Salfred static int err; 11714125Speter 11899783Salfred err = 1; 11999783Salfred if ((err = sm_check_hostname(req, arg->mon_name)) == 0) 12099783Salfred { 12114125Speter res.res_stat = stat_fail; 12214125Speter } 12399783Salfred if (err != 0) 12499783Salfred { 12599783Salfred if (debug) 12699783Salfred syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name); 127100126Salfred if (getaddrinfo(arg->mon_name, NULL, NULL, &ai) == 0) { 12899783Salfred res.res_stat = stat_succ; 129100126Salfred freeaddrinfo(ai); 130100126Salfred } 13199783Salfred else 13299783Salfred { 133168276Smatteo claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ; 13499795Salfred syslog(LOG_ERR, "invalid hostname to sm_stat from %s: %s", 135168276Smatteo from_addr(claddr), arg->mon_name); 13699783Salfred res.res_stat = stat_fail; 13799783Salfred } 13899783Salfred } 13914125Speter res.state = status_info->ourState; 14014125Speter return (&res); 14114125Speter} 14214125Speter 14314125Speter/* sm_mon_1 ---------------------------------------------------------------- */ 14414125Speter/* 14514125Speter Purpose: RPC procedure to establish a monitor request 14614125Speter Returns: Success, unless lack of resources prevents 14714125Speter the necessary structures from being set up 14814125Speter to record the request, or if the hostname is not 149100126Salfred valid (as judged by getaddrinfo()) 15014125Speter*/ 15114125Speter 15214982Speterstruct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req) 15314125Speter{ 15414125Speter static sm_stat_res res; 15514125Speter HostInfo *hp; 15699783Salfred static int err; 15714125Speter MonList *lp; 158100126Salfred struct addrinfo *ai; 15914125Speter 16099783Salfred if ((err = sm_check_hostname(req, arg->mon_id.mon_name)) == 0) 16114125Speter { 16299783Salfred res.res_stat = stat_fail; 16314125Speter } 16414125Speter 16599783Salfred if (err != 0) 16614125Speter { 16799783Salfred if (debug) 16814125Speter { 16999783Salfred syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name); 17099783Salfred syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d", 171161228Sthomas arg->mon_id.my_id.my_name, 172161228Sthomas arg->mon_id.my_id.my_prog, 173161228Sthomas arg->mon_id.my_id.my_vers, 17499783Salfred arg->mon_id.my_id.my_proc); 17514125Speter } 17699783Salfred res.res_stat = stat_fail; /* Assume fail until set otherwise */ 17799783Salfred res.state = status_info->ourState; 17899783Salfred 17999783Salfred /* Find existing host entry, or create one if not found */ 18099783Salfred /* If find_host() fails, it will have logged the error already. */ 181100126Salfred if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0) 18214125Speter { 18399783Salfred syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name); 184100126Salfred return (&res); 18599783Salfred } 186100126Salfred freeaddrinfo(ai); 187100126Salfred if ((hp = find_host(arg->mon_id.mon_name, TRUE))) 18899783Salfred { 18999783Salfred lp = (MonList *)malloc(sizeof(MonList)); 19099783Salfred if (!lp) 19199783Salfred { 19299783Salfred syslog(LOG_ERR, "Out of memory"); 19399783Salfred } 19499783Salfred else 19599783Salfred { 19699783Salfred strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN); 19799783Salfred lp->notifyProg = arg->mon_id.my_id.my_prog; 19899783Salfred lp->notifyVers = arg->mon_id.my_id.my_vers; 19999783Salfred lp->notifyProc = arg->mon_id.my_id.my_proc; 20099783Salfred memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData)); 20114125Speter 20299783Salfred lp->next = hp->monList; 20399783Salfred hp->monList = lp; 20499783Salfred sync_file(); 20514125Speter 20699783Salfred res.res_stat = stat_succ; /* Report success */ 20799783Salfred } 20814125Speter } 20914125Speter } 21014125Speter return (&res); 21114125Speter} 21214125Speter 21314125Speter/* do_unmon ---------------------------------------------------------------- */ 21414125Speter/* 21514125Speter Purpose: Remove a monitor request from a host 21614125Speter Returns: TRUE if found, FALSE if not found. 21714982Speter Notes: Common code from sm_unmon_1_svc and sm_unmon_all_1_svc 21814125Speter In the unlikely event of more than one identical monitor 21914125Speter request, all are removed. 22014125Speter*/ 22114125Speter 22214125Speterstatic int do_unmon(HostInfo *hp, my_id *idp) 22314125Speter{ 22414125Speter MonList *lp, *next; 22514125Speter MonList *last = NULL; 22614125Speter int result = FALSE; 22714125Speter 22814125Speter lp = hp->monList; 22914125Speter while (lp) 23014125Speter { 23114125Speter if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN) 23214125Speter && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc) 23314125Speter && (idp->my_vers == lp->notifyVers)) 23414125Speter { 23514125Speter /* found one. Unhook from chain and free. */ 23614125Speter next = lp->next; 23714125Speter if (last) last->next = next; 23814125Speter else hp->monList = next; 23914125Speter free(lp); 24014125Speter lp = next; 24114125Speter result = TRUE; 24214125Speter } 24314125Speter else 24414125Speter { 24514125Speter last = lp; 24614125Speter lp = lp->next; 24714125Speter } 24814125Speter } 24914125Speter return (result); 25014125Speter} 25114125Speter 25214125Speter/* sm_unmon_1 -------------------------------------------------------------- */ 25314125Speter/* 25414125Speter Purpose: RPC procedure to release a monitor request. 25514125Speter Returns: Local machine's status number 25614125Speter Notes: The supplied mon_id should match the value passed in an 25714125Speter earlier call to sm_mon_1 25814125Speter*/ 25914125Speter 26099798Salfredstruct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req __unused) 26114125Speter{ 26214125Speter static sm_stat res; 26314125Speter HostInfo *hp; 26414125Speter 26514125Speter if (debug) 26614125Speter { 26714125Speter syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name); 26814125Speter syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d", 26999790Salfred arg->mon_name, 27099790Salfred arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc); 27114125Speter } 27214125Speter 27330376Scharnier if ((hp = find_host(arg->mon_name, FALSE))) 27414125Speter { 27514125Speter if (do_unmon(hp, &arg->my_id)) sync_file(); 27614125Speter else 27714125Speter { 27814125Speter syslog(LOG_ERR, "unmon request from %s, no matching monitor", 27914125Speter arg->my_id.my_name); 28014125Speter } 28114125Speter } 28214125Speter else syslog(LOG_ERR, "unmon request from %s for unknown host %s", 28314125Speter arg->my_id.my_name, arg->mon_name); 28414125Speter 28514125Speter res.state = status_info->ourState; 28614125Speter 28714125Speter return (&res); 28814125Speter} 28914125Speter 29014125Speter/* sm_unmon_all_1 ---------------------------------------------------------- */ 29114125Speter/* 29214125Speter Purpose: RPC procedure to release monitor requests. 29314125Speter Returns: Local machine's status number 29414125Speter Notes: Releases all monitor requests (if any) from the specified 29514125Speter host and program number. 29614125Speter*/ 29714125Speter 29899798Salfredstruct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req __unused) 29914125Speter{ 30014125Speter static sm_stat res; 30114125Speter HostInfo *hp; 30214125Speter int i; 30314125Speter 30414125Speter if (debug) 30514125Speter { 30614125Speter syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d", 30714125Speter arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc); 30814125Speter } 30914125Speter 31014125Speter for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++) 31114125Speter { 31214125Speter do_unmon(hp, arg); 31314125Speter } 31414125Speter sync_file(); 31514125Speter 31614125Speter res.state = status_info->ourState; 31714125Speter 31814125Speter return (&res); 31914125Speter} 32014125Speter 32114125Speter/* sm_simu_crash_1 --------------------------------------------------------- */ 32214125Speter/* 32314125Speter Purpose: RPC procedure to simulate a crash 32414125Speter Returns: Nothing 32514125Speter Notes: Standardised mechanism for debug purposes 32614125Speter The specification says that we should drop all of our 32714125Speter status information (apart from the list of monitored hosts 32814125Speter on disc). However, this would confuse the rpc.lockd 32914125Speter which would be unaware that all of its monitor requests 33014125Speter had been silently junked. Hence we in fact retain all 33114125Speter current requests and simply increment the status counter 33214125Speter and inform all hosts on the monitor list. 33314125Speter*/ 33414125Speter 33599804Salfredvoid *sm_simu_crash_1_svc(void *v __unused, struct svc_req *req __unused) 33614125Speter{ 33714125Speter static char dummy; 33814125Speter int work_to_do; 33914125Speter HostInfo *hp; 34014125Speter int i; 34114125Speter 342162263Scharnier work_to_do = FALSE; 34314125Speter if (debug) syslog(LOG_DEBUG, "simu_crash called!!"); 34414125Speter 34514125Speter /* Simulate crash by setting notify-required flag on all monitored */ 34614125Speter /* hosts, and incrementing our status number. notify_hosts() is */ 34714125Speter /* then called to fork a process to do the notifications. */ 34814125Speter 34914125Speter for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++) 35014125Speter { 35114125Speter if (hp->monList) 35214125Speter { 35314125Speter work_to_do = TRUE; 35414125Speter hp->notifyReqd = TRUE; 35514125Speter } 35614125Speter } 35714125Speter status_info->ourState += 2; /* always even numbers if not crashed */ 35814125Speter 35914125Speter if (work_to_do) notify_hosts(); 36014125Speter 36114125Speter return (&dummy); 36214125Speter} 36314125Speter 36414125Speter/* sm_notify_1 ------------------------------------------------------------- */ 36514125Speter/* 36614125Speter Purpose: RPC procedure notifying local statd of the crash of another 36714125Speter Returns: Nothing 36814125Speter Notes: There is danger of deadlock, since it is quite likely that 36914125Speter the client procedure that we call will in turn call us 37014125Speter to remove or adjust the monitor request. 37114125Speter We therefore fork() a process to do the notifications. 37214125Speter Note that the main HostInfo structure is in a mmap() 37314125Speter region and so will be shared with the child, but the 37414125Speter monList pointed to by the HostInfo is in normal memory. 37514125Speter Hence if we read the monList before forking, we are 37614125Speter protected from the parent servicing other requests 37714125Speter that modify the list. 37814125Speter*/ 37914125Speter 38099798Salfredvoid *sm_notify_1_svc(stat_chge *arg, struct svc_req *req __unused) 38114125Speter{ 38214125Speter struct timeval timeout = { 20, 0 }; /* 20 secs timeout */ 38314125Speter CLIENT *cli; 38414125Speter static char dummy; 38592970Salfred sm_status tx_arg; /* arg sent to callback procedure */ 38614125Speter MonList *lp; 38714125Speter HostInfo *hp; 38814125Speter pid_t pid; 38914125Speter 39014125Speter if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d", 39114125Speter arg->mon_name, arg->state); 39214125Speter 39314125Speter hp = find_host(arg->mon_name, FALSE); 39414125Speter if (!hp) 39514125Speter { 39614125Speter /* Never heard of this host - why is it notifying us? */ 39714125Speter syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name); 39899791Salfred return (&dummy); 39914125Speter } 40014125Speter lp = hp->monList; 40199791Salfred if (!lp) return (&dummy); /* We know this host, but have no */ 40214125Speter /* outstanding requests. */ 40314125Speter pid = fork(); 40414125Speter if (pid == -1) 40514125Speter { 40614125Speter syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno)); 40799791Salfred return (NULL); /* no answer, the client will retry */ 40814125Speter } 40914125Speter if (pid) return (&dummy); /* Parent returns */ 41014125Speter 41114125Speter while (lp) 41214125Speter { 41314125Speter tx_arg.mon_name = arg->mon_name; 41414125Speter tx_arg.state = arg->state; 41514125Speter memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv)); 41614125Speter cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp"); 41714125Speter if (!cli) 41814125Speter { 41914125Speter syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost, 42014125Speter clnt_spcreateerror("")); 42114125Speter } 42214125Speter else 42314125Speter { 424121560Speter if (clnt_call(cli, lp->notifyProc, (xdrproc_t)xdr_sm_status, &tx_arg, 425121560Speter (xdrproc_t)xdr_void, &dummy, timeout) != RPC_SUCCESS) 42614125Speter { 42714125Speter syslog(LOG_ERR, "Failed to call rpc.statd client at host %s", 42814125Speter lp->notifyHost); 42914125Speter } 43014125Speter clnt_destroy(cli); 43114125Speter } 43214125Speter lp = lp->next; 43314125Speter } 43414125Speter 43514125Speter exit (0); /* Child quits */ 43614125Speter} 437