procs.c revision 14982
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
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <errno.h>
39#include <rpc/rpc.h>
40#include <syslog.h>
41#include <netdb.h>	/* for gethostbyname()		*/
42
43#include "statd.h"
44
45/* sm_stat_1 --------------------------------------------------------------- */
46/*
47   Purpose:	RPC call to enquire if a host can be monitored
48   Returns:	TRUE for any hostname that can be looked up to give
49		an address.
50*/
51
52struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req)
53{
54  static sm_stat_res res;
55
56  if (debug) syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
57
58  if (gethostbyname(arg->mon_name)) res.res_stat = stat_succ;
59  else
60  {
61    syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name);
62    res.res_stat = stat_fail;
63  }
64
65  res.state = status_info->ourState;
66  return (&res);
67}
68
69/* sm_mon_1 ---------------------------------------------------------------- */
70/*
71   Purpose:	RPC procedure to establish a monitor request
72   Returns:	Success, unless lack of resources prevents
73		the necessary structures from being set up
74		to record the request, or if the hostname is not
75		valid (as judged by gethostbyname())
76*/
77
78struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
79{
80  static sm_stat_res res;
81  HostInfo *hp;
82  MonList *lp;
83
84  if (debug)
85  {
86    syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
87    syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
88      arg->mon_id.mon_name, arg->mon_id.my_id.my_name,
89      arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers,
90      arg->mon_id.my_id.my_proc);
91  }
92
93  res.res_stat = stat_fail;	/* Assume fail until set otherwise	*/
94  res.state = status_info->ourState;
95
96  /* Find existing host entry, or create one if not found		*/
97  /* If find_host() fails, it will have logged the error already.	*/
98  if (!gethostbyname(arg->mon_id.mon_name))
99  {
100    syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
101  }
102  else if (hp = find_host(arg->mon_id.mon_name, TRUE))
103  {
104    lp = (MonList *)malloc(sizeof(MonList));
105    if (!lp)
106    {
107      syslog(LOG_ERR, "Out of memory");
108    }
109    else
110    {
111      strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
112      lp->notifyProg = arg->mon_id.my_id.my_prog;
113      lp->notifyVers = arg->mon_id.my_id.my_vers;
114      lp->notifyProc = arg->mon_id.my_id.my_proc;
115      memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
116
117      lp->next = hp->monList;
118      hp->monList = lp;
119      sync_file();
120
121      res.res_stat = stat_succ;	/* Report success			*/
122    }
123  }
124
125  return (&res);
126}
127
128/* do_unmon ---------------------------------------------------------------- */
129/*
130   Purpose:	Remove a monitor request from a host
131   Returns:	TRUE if found, FALSE if not found.
132   Notes:	Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
133		In the unlikely event of more than one identical monitor
134		request, all are removed.
135*/
136
137static int do_unmon(HostInfo *hp, my_id *idp)
138{
139  MonList *lp, *next;
140  MonList *last = NULL;
141  int result = FALSE;
142
143  lp = hp->monList;
144  while (lp)
145  {
146    if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
147      && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
148      && (idp->my_vers == lp->notifyVers))
149    {
150      /* found one.  Unhook from chain and free.		*/
151      next = lp->next;
152      if (last) last->next = next;
153      else hp->monList = next;
154      free(lp);
155      lp = next;
156      result = TRUE;
157    }
158    else
159    {
160      last = lp;
161      lp = lp->next;
162    }
163  }
164  return (result);
165}
166
167/* sm_unmon_1 -------------------------------------------------------------- */
168/*
169   Purpose:	RPC procedure to release a monitor request.
170   Returns:	Local machine's status number
171   Notes:	The supplied mon_id should match the value passed in an
172		earlier call to sm_mon_1
173*/
174
175struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req)
176{
177  static sm_stat res;
178  HostInfo *hp;
179
180  if (debug)
181  {
182    syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
183    syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
184      arg->mon_name, arg->my_id.my_name, arg->my_id.my_prog,
185      arg->my_id.my_vers, arg->my_id.my_proc);
186  }
187
188  if (hp = find_host(arg->mon_name, FALSE))
189  {
190    if (do_unmon(hp, &arg->my_id)) sync_file();
191    else
192    {
193      syslog(LOG_ERR, "unmon request from %s, no matching monitor",
194	arg->my_id.my_name);
195    }
196  }
197  else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
198    arg->my_id.my_name, arg->mon_name);
199
200  res.state = status_info->ourState;
201
202  return (&res);
203}
204
205/* sm_unmon_all_1 ---------------------------------------------------------- */
206/*
207   Purpose:	RPC procedure to release monitor requests.
208   Returns:	Local machine's status number
209   Notes:	Releases all monitor requests (if any) from the specified
210		host and program number.
211*/
212
213struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req)
214{
215  static sm_stat res;
216  HostInfo *hp;
217  MonList *lp;
218  int i;
219
220  if (debug)
221  {
222    syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
223      arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
224  }
225
226  for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
227  {
228    do_unmon(hp, arg);
229  }
230  sync_file();
231
232  res.state = status_info->ourState;
233
234  return (&res);
235}
236
237/* sm_simu_crash_1 --------------------------------------------------------- */
238/*
239   Purpose:	RPC procedure to simulate a crash
240   Returns:	Nothing
241   Notes:	Standardised mechanism for debug purposes
242		The specification says that we should drop all of our
243		status information (apart from the list of monitored hosts
244		on disc).  However, this would confuse the rpc.lockd
245		which would be unaware that all of its monitor requests
246		had been silently junked.  Hence we in fact retain all
247		current requests and simply increment the status counter
248		and inform all hosts on the monitor list.
249*/
250
251void *sm_simu_crash_1_svc(void *v, struct svc_req *req)
252{
253  static char dummy;
254  int work_to_do;
255  HostInfo *hp;
256  int i;
257
258  if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
259
260  /* Simulate crash by setting notify-required flag on all monitored	*/
261  /* hosts, and incrementing our status number.  notify_hosts() is	*/
262  /* then called to fork a process to do the notifications.		*/
263
264  for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
265  {
266    if (hp->monList)
267    {
268      work_to_do = TRUE;
269      hp->notifyReqd = TRUE;
270    }
271  }
272  status_info->ourState += 2;	/* always even numbers if not crashed	*/
273
274  if (work_to_do) notify_hosts();
275
276  return (&dummy);
277}
278
279/* sm_notify_1 ------------------------------------------------------------- */
280/*
281   Purpose:	RPC procedure notifying local statd of the crash of another
282   Returns:	Nothing
283   Notes:	There is danger of deadlock, since it is quite likely that
284		the client procedure that we call will in turn call us
285		to remove or adjust the monitor request.
286		We therefore fork() a process to do the notifications.
287		Note that the main HostInfo structure is in a mmap()
288		region and so will be shared with the child, but the
289		monList pointed to by the HostInfo is in normal memory.
290		Hence if we read the monList before forking, we are
291		protected from the parent servicing other requests
292		that modify the list.
293*/
294
295void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req)
296{
297  struct timeval timeout = { 20, 0 };	/* 20 secs timeout		*/
298  CLIENT *cli;
299  static char dummy;
300  status tx_arg;		/* arg sent to callback procedure	*/
301  MonList *lp;
302  HostInfo *hp;
303  pid_t pid;
304
305  if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
306    arg->mon_name, arg->state);
307
308  hp = find_host(arg->mon_name, FALSE);
309  if (!hp)
310  {
311    /* Never heard of this host - why is it notifying us?		*/
312    syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
313    return;
314  }
315  lp = hp->monList;
316  if (!lp) return (FALSE);	/* We know this host, but have no	*/
317				/* outstanding requests.		*/
318  pid = fork();
319  if (pid == -1)
320  {
321    syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
322    return;
323  }
324  if (pid) return (&dummy);	/* Parent returns			*/
325
326  while (lp)
327  {
328    tx_arg.mon_name = arg->mon_name;
329    tx_arg.state = arg->state;
330    memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
331    cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
332    if (!cli)
333    {
334      syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
335        clnt_spcreateerror(""));
336    }
337    else
338    {
339      if (clnt_call(cli, lp->notifyProc, xdr_status, &tx_arg, xdr_void, &dummy,
340        timeout) != RPC_SUCCESS)
341      {
342        syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
343	  lp->notifyHost);
344      }
345      clnt_destroy(cli);
346    }
347    lp = lp->next;
348  }
349
350  exit (0);	/* Child quits	*/
351}
352
353