procs.c revision 30376
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
35static 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
57struct 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
83struct 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
142static 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
180struct 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
218struct 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
255void *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
299void *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