xref: /netbsd-src/usr.sbin/rpc.statd/stat_proc.c (revision 421949a31fb0942d3d87278998c2d7d432d8b3cb)
1*421949a3Ssevan /*	$NetBSD: stat_proc.c,v 1.9 2018/01/23 21:06:26 sevan Exp $	*/
224ce527bSscottr 
324ce527bSscottr /*
424ce527bSscottr  * Copyright (c) 1995
524ce527bSscottr  *	A.R. Gordon (andrew.gordon@net-tel.co.uk).  All rights reserved.
624ce527bSscottr  *
724ce527bSscottr  * Redistribution and use in source and binary forms, with or without
824ce527bSscottr  * modification, are permitted provided that the following conditions
924ce527bSscottr  * are met:
1024ce527bSscottr  * 1. Redistributions of source code must retain the above copyright
1124ce527bSscottr  *    notice, this list of conditions and the following disclaimer.
1224ce527bSscottr  * 2. Redistributions in binary form must reproduce the above copyright
1324ce527bSscottr  *    notice, this list of conditions and the following disclaimer in the
1424ce527bSscottr  *    documentation and/or other materials provided with the distribution.
1524ce527bSscottr  * 3. All advertising materials mentioning features or use of this software
1624ce527bSscottr  *    must display the following acknowledgement:
1724ce527bSscottr  *	This product includes software developed for the FreeBSD project
1824ce527bSscottr  * 4. Neither the name of the author nor the names of any co-contributors
1924ce527bSscottr  *    may be used to endorse or promote products derived from this software
2024ce527bSscottr  *    without specific prior written permission.
2124ce527bSscottr  *
2224ce527bSscottr  * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
2324ce527bSscottr  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2424ce527bSscottr  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2524ce527bSscottr  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2624ce527bSscottr  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2724ce527bSscottr  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2824ce527bSscottr  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2924ce527bSscottr  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3024ce527bSscottr  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3124ce527bSscottr  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3224ce527bSscottr  * SUCH DAMAGE.
3324ce527bSscottr  *
3424ce527bSscottr  */
3524ce527bSscottr 
3690a9b36aSlukem #include <sys/cdefs.h>
3790a9b36aSlukem #ifndef lint
38*421949a3Ssevan __RCSID("$NetBSD: stat_proc.c,v 1.9 2018/01/23 21:06:26 sevan Exp $");
3990a9b36aSlukem #endif
4090a9b36aSlukem 
41021be7fdSlukem #include <errno.h>
42021be7fdSlukem #include <netdb.h>
4324ce527bSscottr #include <stdio.h>
4424ce527bSscottr #include <stdlib.h>
4524ce527bSscottr #include <string.h>
4624ce527bSscottr #include <syslog.h>
471175f55bSchristos #include <signal.h>
48021be7fdSlukem #include <unistd.h>
49021be7fdSlukem 
50021be7fdSlukem #include <rpc/rpc.h>
5124ce527bSscottr 
5224ce527bSscottr #include "statd.h"
5324ce527bSscottr 
5424ce527bSscottr /* sm_stat_1 --------------------------------------------------------------- */
5524ce527bSscottr /*
5624ce527bSscottr  * Purpose:	RPC call to enquire if a host can be monitored
5724ce527bSscottr  * Returns:	TRUE for any hostname that can be looked up to give
5824ce527bSscottr  *		an address.
5924ce527bSscottr  */
6024ce527bSscottr struct sm_stat_res *
sm_stat_1_svc(sm_name * arg,struct svc_req * req)61*421949a3Ssevan sm_stat_1_svc(sm_name *arg, struct svc_req *req)
6224ce527bSscottr {
630466bbb6Slukem 	static sm_stat_res smres;
6477044250Sfvdl 	struct addrinfo *ai;
6524ce527bSscottr 
661175f55bSchristos 	NO_ALARM;
6724ce527bSscottr 	if (debug)
6824ce527bSscottr 		syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
6924ce527bSscottr 
7077044250Sfvdl 	if (getaddrinfo(arg->mon_name, NULL, NULL, &ai) == 0) {
710466bbb6Slukem 		smres.res_stat = stat_succ;
7277044250Sfvdl 		freeaddrinfo(ai);
7377044250Sfvdl 	} else {
7424ce527bSscottr 		syslog(LOG_ERR, "invalid hostname to sm_stat: %s",
7524ce527bSscottr 		    arg->mon_name);
760466bbb6Slukem 		smres.res_stat = stat_fail;
7724ce527bSscottr 	}
7824ce527bSscottr 
790466bbb6Slukem 	smres.state = status_info.ourState;
801175f55bSchristos 	ALARM;
810466bbb6Slukem 	return (&smres);
8224ce527bSscottr }
8324ce527bSscottr 
8424ce527bSscottr /* sm_mon_1 ---------------------------------------------------------------- */
8524ce527bSscottr /*
8624ce527bSscottr  * Purpose:	RPC procedure to establish a monitor request
8724ce527bSscottr  * Returns:	Success, unless lack of resources prevents
8824ce527bSscottr  *		the necessary structures from being set up
8924ce527bSscottr  *		to record the request, or if the hostname is not
9024ce527bSscottr  *		valid (as judged by gethostbyname())
9124ce527bSscottr  */
9224ce527bSscottr struct sm_stat_res *
sm_mon_1_svc(mon * arg,struct svc_req * req)93*421949a3Ssevan sm_mon_1_svc(mon *arg, struct svc_req *req)
9424ce527bSscottr {
950466bbb6Slukem 	static sm_stat_res smres;
9677044250Sfvdl 	struct addrinfo *ai;
971175f55bSchristos 	HostInfo *hp, h;
9824ce527bSscottr 	MonList *lp;
9924ce527bSscottr 
1001175f55bSchristos 	NO_ALARM;
10124ce527bSscottr 	if (debug) {
10224ce527bSscottr 		syslog(LOG_DEBUG, "monitor request for host %s",
10324ce527bSscottr 		    arg->mon_id.mon_name);
10424ce527bSscottr 		syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
105021be7fdSlukem 		    arg->mon_id.my_id.my_name, arg->mon_id.my_id.my_prog,
106021be7fdSlukem 		    arg->mon_id.my_id.my_vers, arg->mon_id.my_id.my_proc);
10724ce527bSscottr 	}
1080466bbb6Slukem 	smres.res_stat = stat_fail;	/* Assume fail until set otherwise */
1090466bbb6Slukem 	smres.state = status_info.ourState;
11024ce527bSscottr 
11124ce527bSscottr 	/*
11224ce527bSscottr 	 * Find existing host entry, or create one if not found.  If
11324ce527bSscottr 	 * find_host() fails, it will have logged the error already.
11424ce527bSscottr 	 */
11577044250Sfvdl 	if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0) {
11624ce527bSscottr 		syslog(LOG_ERR, "Invalid hostname to sm_mon: %s",
11724ce527bSscottr 		    arg->mon_id.mon_name);
1180466bbb6Slukem 		return &smres;
1191175f55bSchristos 	}
1201175f55bSchristos 
12177044250Sfvdl 	freeaddrinfo(ai);
12277044250Sfvdl 
1231175f55bSchristos 	if ((hp = find_host(arg->mon_id.mon_name, &h)) == NULL)
1241175f55bSchristos 		memset(hp = &h, 0, sizeof(h));
1251175f55bSchristos 
12624ce527bSscottr 	lp = (MonList *)malloc(sizeof(MonList));
12724ce527bSscottr 	if (!lp)
12824ce527bSscottr 		syslog(LOG_ERR, "Out of memory");
12924ce527bSscottr 	else {
13024ce527bSscottr 		strncpy(lp->notifyHost, arg->mon_id.my_id.my_name,
13124ce527bSscottr 		    SM_MAXSTRLEN);
13224ce527bSscottr 		lp->notifyProg = arg->mon_id.my_id.my_prog;
13324ce527bSscottr 		lp->notifyVers = arg->mon_id.my_id.my_vers;
13424ce527bSscottr 		lp->notifyProc = arg->mon_id.my_id.my_proc;
13524ce527bSscottr 		memcpy(lp->notifyData, arg->priv,
13624ce527bSscottr 		    sizeof(lp->notifyData));
13724ce527bSscottr 
13824ce527bSscottr 		lp->next = hp->monList;
13924ce527bSscottr 		hp->monList = lp;
1401175f55bSchristos 		change_host(arg->mon_id.mon_name, hp);
14124ce527bSscottr 		sync_file();
1420466bbb6Slukem 		smres.res_stat = stat_succ;	/* Report success */
14324ce527bSscottr 	}
1441175f55bSchristos 	ALARM;
1450466bbb6Slukem 	return (&smres);
14624ce527bSscottr }
14724ce527bSscottr 
14824ce527bSscottr /* do_unmon ---------------------------------------------------------------- */
14924ce527bSscottr /*
15024ce527bSscottr  * Purpose:	Remove a monitor request from a host
15124ce527bSscottr  * Returns:	TRUE if found, FALSE if not found.
15224ce527bSscottr  * Notes:	Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
15324ce527bSscottr  *		In the unlikely event of more than one identical monitor
15424ce527bSscottr  *		request, all are removed.
15524ce527bSscottr  */
1561175f55bSchristos int
do_unmon(char * name,HostInfo * hp,void * ptr)157*421949a3Ssevan do_unmon(char *name, HostInfo *hp, void *ptr)
15824ce527bSscottr {
1591175f55bSchristos 	my_id *idp = ptr;
16024ce527bSscottr 	MonList *lp, *next;
16124ce527bSscottr 	MonList *last = NULL;
16224ce527bSscottr 	int result = FALSE;
16324ce527bSscottr 
16424ce527bSscottr 	lp = hp->monList;
16524ce527bSscottr 	while (lp) {
16624ce527bSscottr 		if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
16724ce527bSscottr 		    && (idp->my_prog == lp->notifyProg)
16824ce527bSscottr 		    && (idp->my_proc == lp->notifyProc)
16924ce527bSscottr 		    && (idp->my_vers == lp->notifyVers)) {
17024ce527bSscottr 			/* found one.  Unhook from chain and free. */
17124ce527bSscottr 			next = lp->next;
17224ce527bSscottr 			if (last)
17324ce527bSscottr 				last->next = next;
17424ce527bSscottr 			else
17524ce527bSscottr 				hp->monList = next;
17624ce527bSscottr 			free(lp);
17724ce527bSscottr 			lp = next;
17824ce527bSscottr 			result = TRUE;
17924ce527bSscottr 		} else {
18024ce527bSscottr 			last = lp;
18124ce527bSscottr 			lp = lp->next;
18224ce527bSscottr 		}
18324ce527bSscottr 	}
18424ce527bSscottr 	return (result);
18524ce527bSscottr }
18624ce527bSscottr 
18724ce527bSscottr /* sm_unmon_1 -------------------------------------------------------------- */
18824ce527bSscottr /*
18924ce527bSscottr  * Purpose:	RPC procedure to release a monitor request.
19024ce527bSscottr  * Returns:	Local machine's status number
19124ce527bSscottr  * Notes:	The supplied mon_id should match the value passed in an
19224ce527bSscottr  *		earlier call to sm_mon_1
19324ce527bSscottr  */
19424ce527bSscottr struct sm_stat *
sm_unmon_1_svc(mon_id * arg,struct svc_req * req)195*421949a3Ssevan sm_unmon_1_svc(mon_id *arg, struct svc_req *req)
19624ce527bSscottr {
1970466bbb6Slukem 	static sm_stat smres;
1981175f55bSchristos 	HostInfo *hp, h;
19924ce527bSscottr 
2001175f55bSchristos 	NO_ALARM;
20124ce527bSscottr 	if (debug) {
20224ce527bSscottr 		syslog(LOG_DEBUG, "un-monitor request for host %s",
20324ce527bSscottr 		    arg->mon_name);
20424ce527bSscottr 		syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
205021be7fdSlukem 		    arg->my_id.my_name, arg->my_id.my_prog,
20624ce527bSscottr 		    arg->my_id.my_vers, arg->my_id.my_proc);
20724ce527bSscottr 	}
2081175f55bSchristos 	if ((hp = find_host(arg->mon_name, &h)) != NULL) {
2091175f55bSchristos 		if (do_unmon(arg->mon_name, hp, &arg->my_id)) {
2101175f55bSchristos 			change_host(arg->mon_name, hp);
21124ce527bSscottr 			sync_file();
2121175f55bSchristos 		}
21324ce527bSscottr 		else
21424ce527bSscottr 			syslog(LOG_ERR,
21524ce527bSscottr 			    "unmon request from %s, no matching monitor",
21624ce527bSscottr 			    arg->my_id.my_name);
21724ce527bSscottr 	} else
21824ce527bSscottr 		syslog(LOG_ERR, "unmon request from %s for unknown host %s",
21924ce527bSscottr 		    arg->my_id.my_name, arg->mon_name);
22024ce527bSscottr 
2210466bbb6Slukem 	smres.state = status_info.ourState;
2221175f55bSchristos 	ALARM;
22324ce527bSscottr 
2240466bbb6Slukem 	return (&smres);
22524ce527bSscottr }
22624ce527bSscottr 
22724ce527bSscottr /* sm_unmon_all_1 ---------------------------------------------------------- */
22824ce527bSscottr /*
22924ce527bSscottr  * Purpose:	RPC procedure to release monitor requests.
23024ce527bSscottr  * Returns:	Local machine's status number
23124ce527bSscottr  * Notes:	Releases all monitor requests (if any) from the specified
23224ce527bSscottr  *		host and program number.
23324ce527bSscottr  */
23424ce527bSscottr struct sm_stat *
sm_unmon_all_1_svc(my_id * arg,struct svc_req * req)235*421949a3Ssevan sm_unmon_all_1_svc(my_id *arg, struct svc_req *req)
23624ce527bSscottr {
2370466bbb6Slukem 	static sm_stat smres;
23824ce527bSscottr 
2391175f55bSchristos 	NO_ALARM;
24024ce527bSscottr 	if (debug) {
24124ce527bSscottr 		syslog(LOG_DEBUG,
24224ce527bSscottr 		    "unmon_all for host: %s prog: %d ver: %d proc: %d",
24324ce527bSscottr 		    arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
24424ce527bSscottr 	}
24524ce527bSscottr 
2461175f55bSchristos 	unmon_hosts();
24724ce527bSscottr 	sync_file();
24824ce527bSscottr 
2490466bbb6Slukem 	smres.state = status_info.ourState;
2501175f55bSchristos 	ALARM;
25124ce527bSscottr 
2520466bbb6Slukem 	return (&smres);
25324ce527bSscottr }
25424ce527bSscottr 
25524ce527bSscottr /* sm_simu_crash_1 --------------------------------------------------------- */
25624ce527bSscottr /*
25724ce527bSscottr  * Purpose:	RPC procedure to simulate a crash
25824ce527bSscottr  * Returns:	Nothing
25924ce527bSscottr  * Notes:	Standardised mechanism for debug purposes
26024ce527bSscottr  *		The specification says that we should drop all of our
26124ce527bSscottr  *		status information (apart from the list of monitored hosts
26224ce527bSscottr  *		on disc).  However, this would confuse the rpc.lockd
26324ce527bSscottr  *		which would be unaware that all of its monitor requests
26424ce527bSscottr  *		had been silently junked.  Hence we in fact retain all
26524ce527bSscottr  *		current requests and simply increment the status counter
26624ce527bSscottr  *		and inform all hosts on the monitor list.
26724ce527bSscottr  */
26824ce527bSscottr void *
sm_simu_crash_1_svc(void * v,struct svc_req * req)269*421949a3Ssevan sm_simu_crash_1_svc(void *v, struct svc_req *req)
27024ce527bSscottr {
27124ce527bSscottr 	static char dummy;
27224ce527bSscottr 
2731175f55bSchristos 	NO_ALARM;
27424ce527bSscottr 	if (debug)
27524ce527bSscottr 		syslog(LOG_DEBUG, "simu_crash called!!");
27624ce527bSscottr 
2771175f55bSchristos 	reset_database();
2781175f55bSchristos 	ALARM;
2791175f55bSchristos 	notify_handler(0);
28024ce527bSscottr 
28124ce527bSscottr 	return (&dummy);
28224ce527bSscottr }
28324ce527bSscottr 
28424ce527bSscottr /* sm_notify_1 ------------------------------------------------------------- */
28524ce527bSscottr /*
28624ce527bSscottr  * Purpose:	RPC procedure notifying local statd of the crash of another
28724ce527bSscottr  * Returns:	Nothing
28824ce527bSscottr  * Notes:	There is danger of deadlock, since it is quite likely that
28924ce527bSscottr  *		the client procedure that we call will in turn call us
29024ce527bSscottr  *		to remove or adjust the monitor request.
29124ce527bSscottr  *		We therefore fork() a process to do the notifications.
29224ce527bSscottr  *		Note that the main HostInfo structure is in a mmap()
29324ce527bSscottr  *		region and so will be shared with the child, but the
29424ce527bSscottr  *		monList pointed to by the HostInfo is in normal memory.
29524ce527bSscottr  *		Hence if we read the monList before forking, we are
29624ce527bSscottr  *		protected from the parent servicing other requests
29724ce527bSscottr  *		that modify the list.
29824ce527bSscottr  */
29924ce527bSscottr void   *
sm_notify_1_svc(stat_chge * arg,struct svc_req * req)300*421949a3Ssevan sm_notify_1_svc(stat_chge *arg, struct svc_req *req)
30124ce527bSscottr {
30224ce527bSscottr 	struct timeval timeout = {20, 0};	/* 20 secs timeout */
30324ce527bSscottr 	CLIENT *cli;
30424ce527bSscottr 	static char dummy;
30524ce527bSscottr 	status tx_arg;		/* arg sent to callback procedure */
30624ce527bSscottr 	MonList *lp;
3071175f55bSchristos 	HostInfo *hp, h;
30824ce527bSscottr 	pid_t pid;
30924ce527bSscottr 
31024ce527bSscottr 	if (debug)
31124ce527bSscottr 		syslog(LOG_DEBUG, "notify from host %s, new state %d",
31224ce527bSscottr 		    arg->mon_name, arg->state);
31324ce527bSscottr 
3141175f55bSchristos 	hp = find_host(arg->mon_name, &h);
31524ce527bSscottr 	if (!hp) {
31624ce527bSscottr 		/* Never heard of this host - why is it notifying us? */
317ff01cf48Sbouyer 		syslog(LOG_DEBUG, "Unsolicited notification from host %s",
31824ce527bSscottr 		    arg->mon_name);
319ff01cf48Sbouyer 		return (&dummy);
32024ce527bSscottr 	}
32124ce527bSscottr 	lp = hp->monList;
32224ce527bSscottr 	if (!lp) /* We know this host, but have no outstanding requests. */
323ff01cf48Sbouyer 		return (&dummy);
32424ce527bSscottr 
32558ce78eeSbouyer 	sync_file();
32624ce527bSscottr 	pid = fork();
32724ce527bSscottr 	if (pid == -1) {
32824ce527bSscottr 		syslog(LOG_ERR, "Unable to fork notify process - %s",
32924ce527bSscottr 		    strerror(errno));
330021be7fdSlukem 		return (FALSE);
33124ce527bSscottr 	}
33224ce527bSscottr 	if (pid)
33324ce527bSscottr 		return (&dummy); /* Parent returns */
33424ce527bSscottr 
33524ce527bSscottr 	while (lp) {
33624ce527bSscottr 		tx_arg.mon_name = arg->mon_name;
33724ce527bSscottr 		tx_arg.state = arg->state;
33824ce527bSscottr 		memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
33924ce527bSscottr 		cli = clnt_create(lp->notifyHost, lp->notifyProg,
34024ce527bSscottr 		    lp->notifyVers, "udp");
34124ce527bSscottr 		if (!cli)
34224ce527bSscottr 			syslog(LOG_ERR, "Failed to contact host %s%s",
34324ce527bSscottr 			    lp->notifyHost, clnt_spcreateerror(""));
34424ce527bSscottr 		else {
34524ce527bSscottr 			if (clnt_call(cli, lp->notifyProc, xdr_status, &tx_arg,
34624ce527bSscottr 			    xdr_void, &dummy, timeout) != RPC_SUCCESS)
34724ce527bSscottr 				syslog(LOG_ERR,
34824ce527bSscottr 				    "Failed to call rpc.statd client at host %s",
34924ce527bSscottr 				    lp->notifyHost);
35024ce527bSscottr 			clnt_destroy(cli);
35124ce527bSscottr 		}
35224ce527bSscottr 		lp = lp->next;
35324ce527bSscottr 	}
35424ce527bSscottr 
35524ce527bSscottr 	exit(0);		/* Child quits */
35624ce527bSscottr }
357