xref: /freebsd/usr.sbin/rpc.statd/procs.c (revision 7f3dea244c40159a41ab22da77a434d7c5b5e85a)
1 /*
2  * Copyright (c) 1995
3  *	A.R. Gordon (andrew.gordon@net-tel.co.uk).  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed for the FreeBSD project
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33 
34 #ifndef lint
35 static const char rcsid[] =
36 	"$Id$";
37 #endif /* not lint */
38 
39 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <rpc/rpc.h>
45 #include <syslog.h>
46 #include <netdb.h>	/* for gethostbyname()		*/
47 
48 #include "statd.h"
49 
50 /* sm_stat_1 --------------------------------------------------------------- */
51 /*
52    Purpose:	RPC call to enquire if a host can be monitored
53    Returns:	TRUE for any hostname that can be looked up to give
54 		an address.
55 */
56 
57 struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req)
58 {
59   static sm_stat_res res;
60 
61   if (debug) syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
62 
63   if (gethostbyname(arg->mon_name)) res.res_stat = stat_succ;
64   else
65   {
66     syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name);
67     res.res_stat = stat_fail;
68   }
69 
70   res.state = status_info->ourState;
71   return (&res);
72 }
73 
74 /* sm_mon_1 ---------------------------------------------------------------- */
75 /*
76    Purpose:	RPC procedure to establish a monitor request
77    Returns:	Success, unless lack of resources prevents
78 		the necessary structures from being set up
79 		to record the request, or if the hostname is not
80 		valid (as judged by gethostbyname())
81 */
82 
83 struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
84 {
85   static sm_stat_res res;
86   HostInfo *hp;
87   MonList *lp;
88 
89   if (debug)
90   {
91     syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
92     syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
93       arg->mon_id.mon_name, arg->mon_id.my_id.my_name,
94       arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers,
95       arg->mon_id.my_id.my_proc);
96   }
97 
98   res.res_stat = stat_fail;	/* Assume fail until set otherwise	*/
99   res.state = status_info->ourState;
100 
101   /* Find existing host entry, or create one if not found		*/
102   /* If find_host() fails, it will have logged the error already.	*/
103   if (!gethostbyname(arg->mon_id.mon_name))
104   {
105     syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
106   }
107   else if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
108   {
109     lp = (MonList *)malloc(sizeof(MonList));
110     if (!lp)
111     {
112       syslog(LOG_ERR, "Out of memory");
113     }
114     else
115     {
116       strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
117       lp->notifyProg = arg->mon_id.my_id.my_prog;
118       lp->notifyVers = arg->mon_id.my_id.my_vers;
119       lp->notifyProc = arg->mon_id.my_id.my_proc;
120       memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
121 
122       lp->next = hp->monList;
123       hp->monList = lp;
124       sync_file();
125 
126       res.res_stat = stat_succ;	/* Report success			*/
127     }
128   }
129 
130   return (&res);
131 }
132 
133 /* do_unmon ---------------------------------------------------------------- */
134 /*
135    Purpose:	Remove a monitor request from a host
136    Returns:	TRUE if found, FALSE if not found.
137    Notes:	Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
138 		In the unlikely event of more than one identical monitor
139 		request, all are removed.
140 */
141 
142 static int do_unmon(HostInfo *hp, my_id *idp)
143 {
144   MonList *lp, *next;
145   MonList *last = NULL;
146   int result = FALSE;
147 
148   lp = hp->monList;
149   while (lp)
150   {
151     if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
152       && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
153       && (idp->my_vers == lp->notifyVers))
154     {
155       /* found one.  Unhook from chain and free.		*/
156       next = lp->next;
157       if (last) last->next = next;
158       else hp->monList = next;
159       free(lp);
160       lp = next;
161       result = TRUE;
162     }
163     else
164     {
165       last = lp;
166       lp = lp->next;
167     }
168   }
169   return (result);
170 }
171 
172 /* sm_unmon_1 -------------------------------------------------------------- */
173 /*
174    Purpose:	RPC procedure to release a monitor request.
175    Returns:	Local machine's status number
176    Notes:	The supplied mon_id should match the value passed in an
177 		earlier call to sm_mon_1
178 */
179 
180 struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req)
181 {
182   static sm_stat res;
183   HostInfo *hp;
184 
185   if (debug)
186   {
187     syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
188     syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
189       arg->mon_name, arg->my_id.my_name, arg->my_id.my_prog,
190       arg->my_id.my_vers, arg->my_id.my_proc);
191   }
192 
193   if ((hp = find_host(arg->mon_name, FALSE)))
194   {
195     if (do_unmon(hp, &arg->my_id)) sync_file();
196     else
197     {
198       syslog(LOG_ERR, "unmon request from %s, no matching monitor",
199 	arg->my_id.my_name);
200     }
201   }
202   else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
203     arg->my_id.my_name, arg->mon_name);
204 
205   res.state = status_info->ourState;
206 
207   return (&res);
208 }
209 
210 /* sm_unmon_all_1 ---------------------------------------------------------- */
211 /*
212    Purpose:	RPC procedure to release monitor requests.
213    Returns:	Local machine's status number
214    Notes:	Releases all monitor requests (if any) from the specified
215 		host and program number.
216 */
217 
218 struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req)
219 {
220   static sm_stat res;
221   HostInfo *hp;
222   int i;
223 
224   if (debug)
225   {
226     syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
227       arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
228   }
229 
230   for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
231   {
232     do_unmon(hp, arg);
233   }
234   sync_file();
235 
236   res.state = status_info->ourState;
237 
238   return (&res);
239 }
240 
241 /* sm_simu_crash_1 --------------------------------------------------------- */
242 /*
243    Purpose:	RPC procedure to simulate a crash
244    Returns:	Nothing
245    Notes:	Standardised mechanism for debug purposes
246 		The specification says that we should drop all of our
247 		status information (apart from the list of monitored hosts
248 		on disc).  However, this would confuse the rpc.lockd
249 		which would be unaware that all of its monitor requests
250 		had been silently junked.  Hence we in fact retain all
251 		current requests and simply increment the status counter
252 		and inform all hosts on the monitor list.
253 */
254 
255 void *sm_simu_crash_1_svc(void *v, struct svc_req *req)
256 {
257   static char dummy;
258   int work_to_do;
259   HostInfo *hp;
260   int i;
261 
262   if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
263 
264   /* Simulate crash by setting notify-required flag on all monitored	*/
265   /* hosts, and incrementing our status number.  notify_hosts() is	*/
266   /* then called to fork a process to do the notifications.		*/
267 
268   for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
269   {
270     if (hp->monList)
271     {
272       work_to_do = TRUE;
273       hp->notifyReqd = TRUE;
274     }
275   }
276   status_info->ourState += 2;	/* always even numbers if not crashed	*/
277 
278   if (work_to_do) notify_hosts();
279 
280   return (&dummy);
281 }
282 
283 /* sm_notify_1 ------------------------------------------------------------- */
284 /*
285    Purpose:	RPC procedure notifying local statd of the crash of another
286    Returns:	Nothing
287    Notes:	There is danger of deadlock, since it is quite likely that
288 		the client procedure that we call will in turn call us
289 		to remove or adjust the monitor request.
290 		We therefore fork() a process to do the notifications.
291 		Note that the main HostInfo structure is in a mmap()
292 		region and so will be shared with the child, but the
293 		monList pointed to by the HostInfo is in normal memory.
294 		Hence if we read the monList before forking, we are
295 		protected from the parent servicing other requests
296 		that modify the list.
297 */
298 
299 void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req)
300 {
301   struct timeval timeout = { 20, 0 };	/* 20 secs timeout		*/
302   CLIENT *cli;
303   static char dummy;
304   status tx_arg;		/* arg sent to callback procedure	*/
305   MonList *lp;
306   HostInfo *hp;
307   pid_t pid;
308 
309   if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
310     arg->mon_name, arg->state);
311 
312   hp = find_host(arg->mon_name, FALSE);
313   if (!hp)
314   {
315     /* Never heard of this host - why is it notifying us?		*/
316     syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
317     return;
318   }
319   lp = hp->monList;
320   if (!lp) return (FALSE);	/* We know this host, but have no	*/
321 				/* outstanding requests.		*/
322   pid = fork();
323   if (pid == -1)
324   {
325     syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
326     return;
327   }
328   if (pid) return (&dummy);	/* Parent returns			*/
329 
330   while (lp)
331   {
332     tx_arg.mon_name = arg->mon_name;
333     tx_arg.state = arg->state;
334     memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
335     cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
336     if (!cli)
337     {
338       syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
339         clnt_spcreateerror(""));
340     }
341     else
342     {
343       if (clnt_call(cli, lp->notifyProc, xdr_status, &tx_arg, xdr_void, &dummy,
344         timeout) != RPC_SUCCESS)
345       {
346         syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
347 	  lp->notifyHost);
348       }
349       clnt_destroy(cli);
350     }
351     lp = lp->next;
352   }
353 
354   exit (0);	/* Child quits	*/
355 }
356