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