]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/rpc.statd/procs.c
This commit was generated by cvs2svn to compensate for changes in r162509,
[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 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <rpc/rpc.h>
43 #include <syslog.h>
44 #include <vis.h>
45 #include <netdb.h>      /* for getaddrinfo()            */
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50
51 #include "statd.h"
52
53 /* sm_check_hostname -------------------------------------------------------- */
54 /*
55  * Purpose: Check `mon_name' member of sm_name struct to ensure that the array
56  * consists only of printable characters.
57  *
58  * Returns: TRUE if hostname is good. FALSE if hostname contains binary or
59  * otherwise non-printable characters.
60  *
61  * Notes: Will syslog(3) to warn of corrupt hostname.
62  */
63
64 int sm_check_hostname(struct svc_req *req, char *arg)
65 {
66   int len, dstlen, ret;
67   struct sockaddr_in *claddr;
68   char *dst;
69
70   len = strlen(arg);
71   dstlen = (4 * len) + 1;
72   dst = malloc(dstlen);
73   claddr = svc_getcaller(req->rq_xprt);
74   ret = 1;
75
76   if (claddr == NULL || dst == NULL)
77   {
78     ret = 0;
79   }
80   else if (strvis(dst, arg, VIS_WHITE) != len)
81   {
82     syslog(LOG_ERR,
83         "sm_stat: client %s hostname %s contained invalid characters.",
84         inet_ntoa(claddr->sin_addr),
85         dst);
86     ret = 0;
87   }
88   free(dst);
89   return (ret);
90 }
91
92 /*  sm_stat_1 --------------------------------------------------------------- */
93 /*
94    Purpose:     RPC call to enquire if a host can be monitored
95    Returns:     TRUE for any hostname that can be looked up to give
96                 an address.
97 */
98
99 struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req)
100 {
101   static sm_stat_res res;
102   struct addrinfo *ai;
103   struct sockaddr_in *claddr;
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 (getaddrinfo(arg->mon_name, NULL, NULL, &ai) == 0) {
116             res.res_stat = stat_succ;
117             freeaddrinfo(ai);
118     }
119     else
120     {
121       claddr = svc_getcaller(req->rq_xprt);
122       syslog(LOG_ERR, "invalid hostname to sm_stat from %s: %s",
123           inet_ntoa(claddr->sin_addr), arg->mon_name);
124       res.res_stat = stat_fail;
125     }
126   }
127   res.state = status_info->ourState;
128   return (&res);
129 }
130
131 /* sm_mon_1 ---------------------------------------------------------------- */
132 /*
133    Purpose:     RPC procedure to establish a monitor request
134    Returns:     Success, unless lack of resources prevents
135                 the necessary structures from being set up
136                 to record the request, or if the hostname is not
137                 valid (as judged by getaddrinfo())
138 */
139
140 struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
141 {
142   static sm_stat_res res;
143   HostInfo *hp;
144   static int err;
145   MonList *lp;
146   struct addrinfo *ai;
147
148   if ((err = sm_check_hostname(req, arg->mon_id.mon_name)) == 0)
149   {
150     res.res_stat = stat_fail;
151   }
152
153   if (err != 0)
154   {
155     if (debug)
156     {
157       syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
158       syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
159       arg->mon_id.my_id.my_name,
160       arg->mon_id.my_id.my_prog,
161       arg->mon_id.my_id.my_vers,
162       arg->mon_id.my_id.my_proc);
163     }
164     res.res_stat = stat_fail;  /* Assume fail until set otherwise      */
165     res.state = status_info->ourState;
166   
167     /* Find existing host entry, or create one if not found            */
168     /* If find_host() fails, it will have logged the error already.    */
169     if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0)
170     {
171       syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
172       return (&res);
173     }
174     freeaddrinfo(ai);
175     if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
176     {
177       lp = (MonList *)malloc(sizeof(MonList));
178       if (!lp)
179       {
180         syslog(LOG_ERR, "Out of memory");
181       }
182       else
183       {
184         strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
185         lp->notifyProg = arg->mon_id.my_id.my_prog;
186         lp->notifyVers = arg->mon_id.my_id.my_vers;
187         lp->notifyProc = arg->mon_id.my_id.my_proc;
188         memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
189
190         lp->next = hp->monList;
191         hp->monList = lp;
192         sync_file();
193
194         res.res_stat = stat_succ;      /* Report success                       */
195       }
196     }
197   }
198   return (&res);
199 }
200
201 /* do_unmon ---------------------------------------------------------------- */
202 /*
203    Purpose:     Remove a monitor request from a host
204    Returns:     TRUE if found, FALSE if not found.
205    Notes:       Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
206                 In the unlikely event of more than one identical monitor
207                 request, all are removed.
208 */
209
210 static int do_unmon(HostInfo *hp, my_id *idp)
211 {
212   MonList *lp, *next;
213   MonList *last = NULL;
214   int result = FALSE;
215
216   lp = hp->monList;
217   while (lp)
218   {
219     if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
220       && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
221       && (idp->my_vers == lp->notifyVers))
222     {
223       /* found one.  Unhook from chain and free.                */
224       next = lp->next;
225       if (last) last->next = next;
226       else hp->monList = next;
227       free(lp);
228       lp = next;
229       result = TRUE;
230     }
231     else
232     {
233       last = lp;
234       lp = lp->next;
235     }
236   }
237   return (result);
238 }
239
240 /* sm_unmon_1 -------------------------------------------------------------- */
241 /*
242    Purpose:     RPC procedure to release a monitor request.
243    Returns:     Local machine's status number
244    Notes:       The supplied mon_id should match the value passed in an
245                 earlier call to sm_mon_1
246 */
247
248 struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req __unused)
249 {
250   static sm_stat res;
251   HostInfo *hp;
252
253   if (debug)
254   {
255     syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
256     syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
257       arg->mon_name,
258       arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc);
259   }
260
261   if ((hp = find_host(arg->mon_name, FALSE)))
262   {
263     if (do_unmon(hp, &arg->my_id)) sync_file();
264     else
265     {
266       syslog(LOG_ERR, "unmon request from %s, no matching monitor",
267         arg->my_id.my_name);
268     }
269   }
270   else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
271     arg->my_id.my_name, arg->mon_name);
272
273   res.state = status_info->ourState;
274
275   return (&res);
276 }
277
278 /* sm_unmon_all_1 ---------------------------------------------------------- */
279 /*
280    Purpose:     RPC procedure to release monitor requests.
281    Returns:     Local machine's status number
282    Notes:       Releases all monitor requests (if any) from the specified
283                 host and program number.
284 */
285
286 struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req __unused)
287 {
288   static sm_stat res;
289   HostInfo *hp;
290   int i;
291
292   if (debug)
293   {
294     syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
295       arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
296   }
297
298   for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
299   {
300     do_unmon(hp, arg);
301   }
302   sync_file();
303
304   res.state = status_info->ourState;
305
306   return (&res);
307 }
308
309 /* sm_simu_crash_1 --------------------------------------------------------- */
310 /*
311    Purpose:     RPC procedure to simulate a crash
312    Returns:     Nothing
313    Notes:       Standardised mechanism for debug purposes
314                 The specification says that we should drop all of our
315                 status information (apart from the list of monitored hosts
316                 on disc).  However, this would confuse the rpc.lockd
317                 which would be unaware that all of its monitor requests
318                 had been silently junked.  Hence we in fact retain all
319                 current requests and simply increment the status counter
320                 and inform all hosts on the monitor list.
321 */
322
323 void *sm_simu_crash_1_svc(void *v __unused, struct svc_req *req __unused)
324 {
325   static char dummy;
326   int work_to_do;
327   HostInfo *hp;
328   int i;
329
330   work_to_do = FALSE;
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 }