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