Merge branch 'vendor/MPFR' into gcc441
[dragonfly.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  * $FreeBSD: src/usr.sbin/rpc.statd/procs.c,v 1.4.2.2 2002/07/11 17:41:28 alfred Exp $
33  * $DragonFly: src/usr.sbin/rpc.statd/procs.c,v 1.3 2005/11/25 00:32:49 swildner Exp $
34  */
35
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <rpc/rpc.h>
42 #include <syslog.h>
43 #include <netdb.h>      /* for gethostbyname()          */
44
45 #include "statd.h"
46
47 /* sm_stat_1 --------------------------------------------------------------- */
48 /*
49    Purpose:     RPC call to enquire if a host can be monitored
50    Returns:     TRUE for any hostname that can be looked up to give
51                 an address.
52 */
53
54 struct sm_stat_res *
55 sm_stat_1_svc(sm_name *arg, struct svc_req *req)
56 {
57   static sm_stat_res res;
58
59   if (debug) syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
60
61   if (gethostbyname(arg->mon_name)) res.res_stat = stat_succ;
62   else
63   { 
64     syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name);
65     res.res_stat = stat_fail;
66   }
67
68   res.state = status_info->ourState;
69   return (&res);
70 }
71
72 /* sm_mon_1 ---------------------------------------------------------------- */
73 /*
74    Purpose:     RPC procedure to establish a monitor request
75    Returns:     Success, unless lack of resources prevents
76                 the necessary structures from being set up
77                 to record the request, or if the hostname is not
78                 valid (as judged by gethostbyname())
79 */
80
81 struct sm_stat_res *
82 sm_mon_1_svc(mon *arg, struct svc_req *req)
83 {
84   static sm_stat_res res;
85   HostInfo *hp;
86   MonList *lp;
87
88   if (debug)
89   {
90     syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
91     syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
92       arg->mon_id.mon_name, arg->mon_id.my_id.my_name,
93       arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers,
94       arg->mon_id.my_id.my_proc);
95   }
96
97   res.res_stat = stat_fail;     /* Assume fail until set otherwise      */
98   res.state = status_info->ourState;
99
100   /* Find existing host entry, or create one if not found               */
101   /* If find_host() fails, it will have logged the error already.       */
102   if (!gethostbyname(arg->mon_id.mon_name))
103   {
104     syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
105   }
106   else if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
107   {
108     lp = (MonList *)malloc(sizeof(MonList));
109     if (!lp)
110     {
111       syslog(LOG_ERR, "Out of memory");
112     }
113     else
114     {
115       strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
116       lp->notifyProg = arg->mon_id.my_id.my_prog;
117       lp->notifyVers = arg->mon_id.my_id.my_vers;
118       lp->notifyProc = arg->mon_id.my_id.my_proc;
119       memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
120
121       lp->next = hp->monList;
122       hp->monList = lp;
123       sync_file();
124
125       res.res_stat = stat_succ; /* Report success                       */
126     }
127   }
128
129   return (&res);
130 }
131
132 /* do_unmon ---------------------------------------------------------------- */
133 /*
134    Purpose:     Remove a monitor request from a host
135    Returns:     TRUE if found, FALSE if not found.
136    Notes:       Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
137                 In the unlikely event of more than one identical monitor
138                 request, all are removed.
139 */
140
141 static int
142 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
180 struct sm_stat *
181 sm_unmon_1_svc(mon_id *arg, struct svc_req *req)
182 {
183   static sm_stat res;
184   HostInfo *hp;
185
186   if (debug)
187   {
188     syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
189     syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
190       arg->mon_name, arg->my_id.my_name, arg->my_id.my_prog,
191       arg->my_id.my_vers, arg->my_id.my_proc);
192   }
193
194   if ((hp = find_host(arg->mon_name, FALSE)))
195   {
196     if (do_unmon(hp, &arg->my_id)) sync_file();
197     else
198     {
199       syslog(LOG_ERR, "unmon request from %s, no matching monitor",
200         arg->my_id.my_name);
201     }
202   }
203   else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
204     arg->my_id.my_name, arg->mon_name);
205
206   res.state = status_info->ourState;
207
208   return (&res);
209 }
210
211 /* sm_unmon_all_1 ---------------------------------------------------------- */
212 /*
213    Purpose:     RPC procedure to release monitor requests.
214    Returns:     Local machine's status number
215    Notes:       Releases all monitor requests (if any) from the specified
216                 host and program number.
217 */
218
219 struct sm_stat *
220 sm_unmon_all_1_svc(my_id *arg, struct svc_req *req)
221 {
222   static sm_stat res;
223   HostInfo *hp;
224   int i;
225
226   if (debug)
227   {
228     syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
229       arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
230   }
231
232   for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
233   {
234     do_unmon(hp, arg);
235   }
236   sync_file();
237
238   res.state = status_info->ourState;
239
240   return (&res);
241 }
242
243 /* sm_simu_crash_1 --------------------------------------------------------- */
244 /*
245    Purpose:     RPC procedure to simulate a crash
246    Returns:     Nothing
247    Notes:       Standardised mechanism for debug purposes
248                 The specification says that we should drop all of our
249                 status information (apart from the list of monitored hosts
250                 on disc).  However, this would confuse the rpc.lockd
251                 which would be unaware that all of its monitor requests
252                 had been silently junked.  Hence we in fact retain all
253                 current requests and simply increment the status counter
254                 and inform all hosts on the monitor list.
255 */
256
257 void *
258 sm_simu_crash_1_svc(void *v, struct svc_req *req)
259 {
260   static char dummy;
261   int work_to_do;
262   HostInfo *hp;
263   int i;
264
265   if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
266
267   /* Simulate crash by setting notify-required flag on all monitored    */
268   /* hosts, and incrementing our status number.  notify_hosts() is      */
269   /* then called to fork a process to do the notifications.             */
270
271   for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
272   {
273     if (hp->monList)
274     {
275       work_to_do = TRUE;
276       hp->notifyReqd = TRUE;
277     }
278   }
279   status_info->ourState += 2;   /* always even numbers if not crashed   */
280
281   if (work_to_do) notify_hosts();
282
283   return (&dummy);
284 }
285
286 /* sm_notify_1 ------------------------------------------------------------- */
287 /*
288    Purpose:     RPC procedure notifying local statd of the crash of another
289    Returns:     Nothing
290    Notes:       There is danger of deadlock, since it is quite likely that
291                 the client procedure that we call will in turn call us
292                 to remove or adjust the monitor request.
293                 We therefore fork() a process to do the notifications.
294                 Note that the main HostInfo structure is in a mmap()
295                 region and so will be shared with the child, but the
296                 monList pointed to by the HostInfo is in normal memory.
297                 Hence if we read the monList before forking, we are
298                 protected from the parent servicing other requests
299                 that modify the list.
300 */
301
302 void *
303 sm_notify_1_svc(stat_chge *arg, struct svc_req *req)
304 {
305   struct timeval timeout = { 20, 0 };   /* 20 secs timeout              */
306   CLIENT *cli;
307   static char dummy; 
308   sm_status tx_arg;             /* arg sent to callback procedure       */
309   MonList *lp;
310   HostInfo *hp;
311   pid_t pid;
312
313   if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
314     arg->mon_name, arg->state);
315
316   hp = find_host(arg->mon_name, FALSE);
317   if (!hp)
318   {
319     /* Never heard of this host - why is it notifying us?               */
320     syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
321     return (&dummy);
322   }
323   lp = hp->monList;
324   if (!lp) return (&dummy);     /* We know this host, but have no       */
325                                 /* outstanding requests.                */
326   pid = fork();
327   if (pid == -1)
328   {
329     syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
330     return (NULL);
331   }
332   if (pid) return (&dummy);     /* Parent returns                       */
333
334   while (lp)
335   {
336     tx_arg.mon_name = arg->mon_name;
337     tx_arg.state = arg->state;
338     memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
339     cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
340     if (!cli)
341     {
342       syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
343         clnt_spcreateerror(""));
344     }
345     else
346     {
347       if (clnt_call(cli, lp->notifyProc, (xdrproc_t)xdr_sm_status, &tx_arg,
348           (xdrproc_t)xdr_void, &dummy, timeout) != RPC_SUCCESS)
349       {
350         syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
351           lp->notifyHost);
352       }
353       clnt_destroy(cli);
354     }
355     lp = lp->next;
356   }
357
358   exit (0);     /* Child quits  */
359 }