]> git.saurik.com Git - apple/network_cmds.git/blob - rpc_statd.tproj/procs.c
network_cmds-176.3.3.tar.gz
[apple/network_cmds.git] / rpc_statd.tproj / 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 = FALSE;
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 /*
385 * Hmmm... We've never heard of this host.
386 * It's possible the host just didn't give us the right hostname.
387 * Let's try the IP address the request came from and any hostnames it has.
388 */
389 struct sockaddr_in *claddr;
390 if ((claddr = svc_getcaller(req->rq_xprt))) {
391 struct hostent *he;
392 he = gethostbyaddr((char*)&claddr->sin_addr, sizeof(claddr->sin_addr), AF_INET);
393 if (he) {
394 char **np = he->h_aliases;
395 hp = find_host(he->h_name, FALSE);
396 while (!hp && *np) {
397 hp = find_host(*np, FALSE);
398 if (!hp)
399 np++;
400 }
401 }
402 if (hp)
403 syslog(LOG_DEBUG, "Notification from host %s found as %s",
404 arg->mon_name, hp->hostname);
405 }
406 if (!hp) {
407 /* Never heard of this host - why is it notifying us? */
408 syslog(LOG_DEBUG, "Unsolicited notification from host %s", arg->mon_name);
409 return (&dummy);
410 }
411 }
412 lp = hp->monList;
413 if (!lp) return (&dummy); /* We know this host, but have no */
414 /* outstanding requests. */
415 pid = fork();
416 if (pid == -1)
417 {
418 syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
419 return (NULL); /* no answer, the client will retry */
420 }
421 if (pid) return (&dummy); /* Parent returns */
422
423 while (lp)
424 {
425 tx_arg.mon_name = hp->hostname;
426 tx_arg.state = arg->state;
427 memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
428 cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
429 if (!cli)
430 {
431 syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
432 clnt_spcreateerror(""));
433 }
434 else
435 {
436 if (clnt_call(cli, lp->notifyProc, xdr_sm_status, &tx_arg, xdr_void,
437 &dummy, timeout) != RPC_SUCCESS)
438 {
439 syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
440 lp->notifyHost);
441 }
442 clnt_destroy(cli);
443 }
444 lp = lp->next;
445 }
446
447 exit (0); /* Child quits */
448 }