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