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