xref: /netbsd-src/usr.sbin/rpc.lockd/lock_proc.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: lock_proc.c,v 1.2 1997/10/18 04:01:15 lukem Exp $	*/
2 
3 /*
4  * Copyright (c) 1995
5  *	A.R. Gordon (andrew.gordon@net-tel.co.uk).  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed for the FreeBSD project
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __RCSID("$NetBSD: lock_proc.c,v 1.2 1997/10/18 04:01:15 lukem Exp $");
39 #endif
40 
41 #include <sys/param.h>
42 #include <sys/socket.h>
43 
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 
47 #include <netdb.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <syslog.h>
51 
52 #include <rpc/rpc.h>
53 #include <rpcsvc/sm_inter.h>
54 
55 #include "lockd.h"
56 #include "nlm_prot.h"
57 
58 
59 #define	CLIENT_CACHE_SIZE	64	/* No. of client sockets cached */
60 #define	CLIENT_CACHE_LIFETIME	120	/* In seconds */
61 
62 static CLIENT  *get_client __P((struct sockaddr_in *));
63 static void	log_from_addr __P((char *, struct svc_req *));
64 static void	transmit_result __P((int, nlm_res *, struct svc_req *));
65 
66 /* log_from_addr ----------------------------------------------------------- */
67 /*
68  * Purpose:	Log name of function called and source address
69  * Returns:	Nothing
70  * Notes:	Extracts the source address from the transport handle
71  *		passed in as part of the called procedure specification
72  */
73 static void
74 log_from_addr(fun_name, req)
75 	char *fun_name;
76 	struct svc_req *req;
77 {
78 	struct sockaddr_in *addr;
79 	struct hostent *host;
80 	char hostname_buf[40];
81 
82 	addr = svc_getcaller(req->rq_xprt);
83 	host = gethostbyaddr((char *)&(addr->sin_addr), addr->sin_len, AF_INET);
84 	if (host) {
85 		strncpy(hostname_buf, host->h_name, sizeof(hostname_buf));
86 		hostname_buf[sizeof(hostname_buf) - 1] = '\0';
87 	} else		/* No hostname available - print raw address */
88 		strcpy(hostname_buf, inet_ntoa(addr->sin_addr));
89 
90 	syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf);
91 }
92 
93 /* get_client -------------------------------------------------------------- */
94 /*
95  * Purpose:	Get a CLIENT* for making RPC calls to lockd on given host
96  * Returns:	CLIENT* pointer, from clnt_udp_create, or NULL if error
97  * Notes:	Creating a CLIENT* is quite expensive, involving a
98  *		conversation with the remote portmapper to get the
99  *		port number.  Since a given client is quite likely
100  *		to make several locking requests in succession, it is
101  *		desirable to cache the created CLIENT*.
102  *
103  *		Since we are using UDP rather than TCP, there is no cost
104  *		to the remote system in keeping these cached indefinitely.
105  *		Unfortunately there is a snag: if the remote system
106  *		reboots, the cached portmapper results will be invalid,
107  *		and we will never detect this since all of the xxx_msg()
108  *		calls return no result - we just fire off a udp packet
109  *		and hope for the best.
110  *
111  *		We solve this by discarding cached values after two
112  *		minutes, regardless of whether they have been used
113  *		in the meanwhile (since a bad one might have been used
114  *		plenty of times, as the host keeps retrying the request
115  *		and we keep sending the reply back to the wrong port).
116  *
117  *		Given that the entries will always expire in the order
118  *		that they were created, there is no point in a LRU
119  *		algorithm for when the cache gets full - entries are
120  *		always re-used in sequence.
121  */
122 static CLIENT *clnt_cache_ptr[CLIENT_CACHE_SIZE];
123 static long clnt_cache_time[CLIENT_CACHE_SIZE];	/* time entry created */
124 static struct in_addr clnt_cache_addr[CLIENT_CACHE_SIZE];
125 static int clnt_cache_next_to_use = 0;
126 
127 static CLIENT *
128 get_client(host_addr)
129 	struct sockaddr_in *host_addr;
130 {
131 	CLIENT *client;
132 	struct timeval retry_time, time_now;
133 	int i, sock_no;
134 
135 	gettimeofday(&time_now, NULL);
136 
137 	/*
138 	 * Search for the given client in the cache, zapping any expired
139 	 * entries that we happen to notice in passing.
140 	 */
141 	for (i = 0; i < CLIENT_CACHE_SIZE; i++) {
142 		client = clnt_cache_ptr[i];
143 		if (client && ((clnt_cache_time[i] + CLIENT_CACHE_LIFETIME)
144 		    < time_now.tv_sec)) {
145 			/* Cache entry has expired. */
146 			if (debug_level > 3)
147 				syslog(LOG_DEBUG, "Expired CLIENT* in cache");
148 			clnt_cache_time[i] = 0L;
149 			clnt_destroy(client);
150 			clnt_cache_ptr[i] = NULL;
151 			client = NULL;
152 		}
153 		if (client && !memcmp(&clnt_cache_addr[i],
154 		    &host_addr->sin_addr, sizeof(struct in_addr))) {
155 			/* Found it! */
156 			if (debug_level > 3)
157 				syslog(LOG_DEBUG, "Found CLIENT* in cache");
158 			return (client);
159 		}
160 	}
161 
162 	/* Not found in cache.  Free the next entry if it is in use. */
163 	if (clnt_cache_ptr[clnt_cache_next_to_use]) {
164 		clnt_destroy(clnt_cache_ptr[clnt_cache_next_to_use]);
165 		clnt_cache_ptr[clnt_cache_next_to_use] = NULL;
166 	}
167 
168 	/* Create the new client handle */
169 	sock_no = RPC_ANYSOCK;
170 	retry_time.tv_sec = 5;
171 	retry_time.tv_usec = 0;
172 	host_addr->sin_port = 0; /* Force consultation with portmapper */
173 	client = clntudp_create(host_addr, NLM_PROG, NLM_VERS,
174 	    retry_time, &sock_no);
175 	if (!client) {
176 		syslog(LOG_ERR, clnt_spcreateerror("clntudp_create"));
177 		syslog(LOG_ERR, "Unable to return result to %s",
178 		    inet_ntoa(host_addr->sin_addr));
179 		return NULL;
180 	}
181 
182 	/* Success - update the cache entry */
183 	clnt_cache_ptr[clnt_cache_next_to_use] = client;
184 	clnt_cache_addr[clnt_cache_next_to_use] = host_addr->sin_addr;
185 	clnt_cache_time[clnt_cache_next_to_use] = time_now.tv_sec;
186 	if (++clnt_cache_next_to_use > CLIENT_CACHE_SIZE)
187 		clnt_cache_next_to_use = 0;
188 
189 	/*
190 	 * Disable the default timeout, so we can specify our own in calls
191 	 * to clnt_call().  (Note that the timeout is a different concept
192 	 * from the retry period set in clnt_udp_create() above.)
193 	 */
194 	retry_time.tv_sec = -1;
195 	retry_time.tv_usec = -1;
196 	clnt_control(client, CLSET_TIMEOUT, (char *)&retry_time);
197 
198 	if (debug_level > 3)
199 		syslog(LOG_DEBUG, "Created CLIENT* for %s",
200 		    inet_ntoa(host_addr->sin_addr));
201 	return client;
202 }
203 
204 
205 /* transmit_result --------------------------------------------------------- */
206 /*
207  * Purpose:	Transmit result for nlm_xxx_msg pseudo-RPCs
208  * Returns:	Nothing - we have no idea if the datagram got there
209  * Notes:	clnt_call() will always fail (with timeout) as we are
210  *		calling it with timeout 0 as a hack to just issue a datagram
211  *		without expecting a result
212  */
213 static void
214 transmit_result(opcode, result, req)
215 	int opcode;
216 	nlm_res *result;
217 	struct svc_req *req;
218 {
219 	static char dummy;
220 	struct sockaddr_in *addr;
221 	CLIENT *cli;
222 	struct timeval timeo;
223 	int success;
224 
225 	addr = svc_getcaller(req->rq_xprt);
226 	if ((cli = get_client(addr)) != NULL) {
227 		timeo.tv_sec = 0; /* No timeout - not expecting response */
228 		timeo.tv_usec = 0;
229 
230 		success = clnt_call(cli, opcode, xdr_nlm_res, result, xdr_void,
231 		    &dummy, timeo);
232 
233 		if (debug_level > 2)
234 			syslog(LOG_DEBUG, "clnt_call returns %d\n", success);
235 	}
236 }
237 /* ------------------------------------------------------------------------- */
238 /*
239  * Functions for Unix<->Unix locking (ie. monitored locking, with rpc.statd
240  * involved to ensure reclaim of locks after a crash of the "stateless"
241  * server.
242  *
243  * These all come in two flavours - nlm_xxx() and nlm_xxx_msg().
244  * The first are standard RPCs with argument and result.
245  * The nlm_xxx_msg() calls implement exactly the same functions, but
246  * use two pseudo-RPCs (one in each direction).  These calls are NOT
247  * standard use of the RPC protocol in that they do not return a result
248  * at all (NB. this is quite different from returning a void result).
249  * The effect of this is to make the nlm_xxx_msg() calls simple unacknowledged
250  * datagrams, requiring higher-level code to perform retries.
251  *
252  * Despite the disadvantages of the nlm_xxx_msg() approach (some of which
253  * are documented in the comments to get_client() above), this is the
254  * interface used by all current commercial NFS implementations
255  * [Solaris, SCO, AIX etc.].  This is presumed to be because these allow
256  * implementations to continue using the standard RPC libraries, while
257  * avoiding the block-until-result nature of the library interface.
258  *
259  * No client implementations have been identified so far that make use
260  * of the true RPC version (early SunOS releases would be a likely candidate
261  * for testing).
262  */
263 
264 /* nlm_test ---------------------------------------------------------------- */
265 /*
266  * Purpose:	Test whether a specified lock would be granted if requested
267  * Returns:	nlm_granted (or error code)
268  * Notes:
269  */
270 nlm_testres *
271 nlm_test_1_svc(arg, rqstp)
272 	nlm_testargs *arg;
273 	struct svc_req *rqstp;
274 {
275 	static nlm_testres res;
276 
277 	if (debug_level)
278 		log_from_addr("nlm_test", rqstp);
279 
280 	/*
281 	 * Copy the cookie from the argument into the result.  Note that this
282 	 * is slightly hazardous, as the structure contains a pointer to a
283 	 * malloc()ed buffer that will get freed by the caller.  However, the
284 	 * main function transmits the result before freeing the argument
285 	 * so it is in fact safe.
286 	 */
287 	res.cookie = arg->cookie;
288 	res.stat.stat = nlm_granted;
289 	return (&res);
290 }
291 
292 void *
293 nlm_test_msg_1_svc(arg, rqstp)
294 	nlm_testargs *arg;
295 	struct svc_req *rqstp;
296 {
297 	nlm_testres res;
298 	static char dummy;
299 	struct sockaddr_in *addr;
300 	CLIENT *cli;
301 	int success;
302 	struct timeval timeo;
303 
304 	if (debug_level)
305 		log_from_addr("nlm_test_msg", rqstp);
306 
307 	res.cookie = arg->cookie;
308 	res.stat.stat = nlm_granted;
309 
310 	/*
311 	 * nlm_test has different result type to the other operations, so
312 	 * can't use transmit_result() in this case
313 	 */
314 	addr = svc_getcaller(rqstp->rq_xprt);
315 	if ((cli = get_client(addr)) != NULL) {
316 		timeo.tv_sec = 0; /* No timeout - not expecting response */
317 		timeo.tv_usec = 0;
318 
319 		success = clnt_call(cli, NLM_TEST_RES, xdr_nlm_testres,
320 		    &res, xdr_void, &dummy, timeo);
321 
322 		if (debug_level > 2)
323 			syslog(LOG_DEBUG, "clnt_call returns %d\n", success);
324 	}
325 	return (NULL);
326 }
327 
328 /* nlm_lock ---------------------------------------------------------------- */
329 /*
330  * Purposes:	Establish a lock
331  * Returns:	granted, denied or blocked
332  * Notes:	*** grace period support missing
333  */
334 nlm_res *
335 nlm_lock_1_svc(arg, rqstp)
336 	nlm_lockargs *arg;
337 	struct svc_req *rqstp;
338 {
339 	static nlm_res res;
340 
341 	if (debug_level)
342 		log_from_addr("nlm_lock", rqstp);
343 
344 	/* copy cookie from arg to result.  See comment in nlm_test_1() */
345 	res.cookie = arg->cookie;
346 
347 	res.stat.stat = nlm_granted;
348 	return (&res);
349 }
350 
351 void *
352 nlm_lock_msg_1_svc(arg, rqstp)
353 	nlm_lockargs *arg;
354 	struct svc_req *rqstp;
355 {
356 	static nlm_res res;
357 
358 	if (debug_level)
359 		log_from_addr("nlm_lock_msg", rqstp);
360 
361 	res.cookie = arg->cookie;
362 	res.stat.stat = nlm_granted;
363 	transmit_result(NLM_LOCK_RES, &res, rqstp);
364 
365 	return (NULL);
366 }
367 
368 /* nlm_cancel -------------------------------------------------------------- */
369 /*
370  * Purpose:	Cancel a blocked lock request
371  * Returns:	granted or denied
372  * Notes:
373  */
374 nlm_res *
375 nlm_cancel_1_svc(arg, rqstp)
376 	nlm_cancargs *arg;
377 	struct svc_req *rqstp;
378 {
379 	static nlm_res res;
380 
381 	if (debug_level)
382 		log_from_addr("nlm_cancel", rqstp);
383 
384 	/* copy cookie from arg to result.  See comment in nlm_test_1() */
385 	res.cookie = arg->cookie;
386 
387 	/*
388 	 * Since at present we never return 'nlm_blocked', there can never be
389 	 * a lock to cancel, so this call always fails.
390 	 */
391 	res.stat.stat = nlm_denied;
392 	return (&res);
393 }
394 
395 void *
396 nlm_cancel_msg_1_svc(arg, rqstp)
397 	nlm_cancargs *arg;
398 	struct svc_req *rqstp;
399 {
400 	static nlm_res res;
401 
402 	if (debug_level)
403 		log_from_addr("nlm_cancel_msg", rqstp);
404 
405 	res.cookie = arg->cookie;
406 	/*
407 	 * Since at present we never return 'nlm_blocked', there can never be
408 	 * a lock to cancel, so this call always fails.
409 	 */
410 	res.stat.stat = nlm_denied;
411 	transmit_result(NLM_CANCEL_RES, &res, rqstp);
412 	return (NULL);
413 }
414 
415 /* nlm_unlock -------------------------------------------------------------- */
416 /*
417  * Purpose:	Release an existing lock
418  * Returns:	Always granted, unless during grace period
419  * Notes:	"no such lock" error condition is ignored, as the
420  *		protocol uses unreliable UDP datagrams, and may well
421  *		re-try an unlock that has already succeeded.
422  */
423 nlm_res *
424 nlm_unlock_1_svc(arg, rqstp)
425 	nlm_unlockargs *arg;
426 	struct svc_req *rqstp;
427 {
428 	static nlm_res res;
429 
430 	if (debug_level)
431 		log_from_addr("nlm_unlock", rqstp);
432 
433 	res.stat.stat = nlm_granted;
434 	res.cookie = arg->cookie;
435 
436 	return (&res);
437 }
438 
439 void *
440 nlm_unlock_msg_1_svc(arg, rqstp)
441 	nlm_unlockargs *arg;
442 	struct svc_req *rqstp;
443 {
444 	static nlm_res res;
445 
446 	if (debug_level)
447 		log_from_addr("nlm_unlock_msg", rqstp);
448 
449 	res.stat.stat = nlm_granted;
450 	res.cookie = arg->cookie;
451 
452 	transmit_result(NLM_UNLOCK_RES, &res, rqstp);
453 	return (NULL);
454 }
455 
456 /* ------------------------------------------------------------------------- */
457 /*
458  * Client-side pseudo-RPCs for results.  Note that for the client there
459  * are only nlm_xxx_msg() versions of each call, since the 'real RPC'
460  * version returns the results in the RPC result, and so the client
461  * does not normally receive incoming RPCs.
462  *
463  * The exception to this is nlm_granted(), which is genuinely an RPC
464  * call from the server to the client - a 'call-back' in normal procedure
465  * call terms.
466  */
467 
468 /* nlm_granted ------------------------------------------------------------- */
469 /*
470  * Purpose:	Receive notification that formerly blocked lock now granted
471  * Returns:	always success ('granted')
472  * Notes:
473  */
474 nlm_res *
475 nlm_granted_1_svc(arg, rqstp)
476 	nlm_testargs *arg;
477 	struct svc_req *rqstp;
478 {
479 	static nlm_res res;
480 
481 	if (debug_level)
482 		log_from_addr("nlm_granted", rqstp);
483 
484 	/* copy cookie from arg to result.  See comment in nlm_test_1() */
485 	res.cookie = arg->cookie;
486 
487 	res.stat.stat = nlm_granted;
488 	return (&res);
489 }
490 
491 void *
492 nlm_granted_msg_1_svc(arg, rqstp)
493 	nlm_testargs *arg;
494 	struct svc_req *rqstp;
495 {
496 	static nlm_res res;
497 
498 	if (debug_level)
499 		log_from_addr("nlm_granted_msg", rqstp);
500 
501 	res.cookie = arg->cookie;
502 	res.stat.stat = nlm_granted;
503 	transmit_result(NLM_GRANTED_RES, &res, rqstp);
504 	return (NULL);
505 }
506 
507 /* nlm_test_res ------------------------------------------------------------ */
508 /*
509  * Purpose:	Accept result from earlier nlm_test_msg() call
510  * Returns:	Nothing
511  */
512 void *
513 nlm_test_res_1_svc(arg, rqstp)
514 	nlm_testres *arg;
515 	struct svc_req *rqstp;
516 {
517 	if (debug_level)
518 		log_from_addr("nlm_test_res", rqstp);
519 	return (NULL);
520 }
521 
522 /* nlm_lock_res ------------------------------------------------------------ */
523 /*
524  * Purpose:	Accept result from earlier nlm_lock_msg() call
525  * Returns:	Nothing
526  */
527 void *
528 nlm_lock_res_1_svc(arg, rqstp)
529 	nlm_res *arg;
530 	struct svc_req *rqstp;
531 {
532 	if (debug_level)
533 		log_from_addr("nlm_lock_res", rqstp);
534 
535 	return (NULL);
536 }
537 
538 /* nlm_cancel_res ---------------------------------------------------------- */
539 /*
540  * Purpose:	Accept result from earlier nlm_cancel_msg() call
541  * Returns:	Nothing
542  */
543 void *
544 nlm_cancel_res_1_svc(arg, rqstp)
545 	nlm_res *arg;
546 	struct svc_req *rqstp;
547 {
548 	if (debug_level)
549 		log_from_addr("nlm_cancel_res", rqstp);
550 	return (NULL);
551 }
552 
553 /* nlm_unlock_res ---------------------------------------------------------- */
554 /*
555  * Purpose:	Accept result from earlier nlm_unlock_msg() call
556  * Returns:	Nothing
557  */
558 void *
559 nlm_unlock_res_1_svc(arg, rqstp)
560 	nlm_res *arg;
561 	struct svc_req *rqstp;
562 {
563 	if (debug_level)
564 		log_from_addr("nlm_unlock_res", rqstp);
565 	return (NULL);
566 }
567 
568 /* nlm_granted_res --------------------------------------------------------- */
569 /*
570  * Purpose:	Accept result from earlier nlm_granted_msg() call
571  * Returns:	Nothing
572  */
573 void *
574 nlm_granted_res_1_svc(arg, rqstp)
575 	nlm_res *arg;
576 	struct svc_req *rqstp;
577 {
578 	if (debug_level)
579 		log_from_addr("nlm_granted_res", rqstp);
580 	return (NULL);
581 }
582 
583 /* ------------------------------------------------------------------------- */
584 /*
585  * Calls for PCNFS locking (aka non-monitored locking, no involvement
586  * of rpc.statd).
587  *
588  * These are all genuine RPCs - no nlm_xxx_msg() nonsense here.
589  */
590 
591 /* nlm_share --------------------------------------------------------------- */
592 /*
593  * Purpose:	Establish a DOS-style lock
594  * Returns:	success or failure
595  * Notes:	Blocking locks are not supported - client is expected
596  *		to retry if required.
597  */
598 nlm_shareres *
599 nlm_share_3_svc(arg, rqstp)
600 	nlm_shareargs *arg;
601 	struct svc_req *rqstp;
602 {
603 	static nlm_shareres res;
604 
605 	if (debug_level)
606 		log_from_addr("nlm_share", rqstp);
607 
608 	res.cookie = arg->cookie;
609 	res.stat = nlm_granted;
610 	res.sequence = 1234356;	/* X/Open says this field is ignored? */
611 	return (&res);
612 }
613 
614 /* nlm_unshare ------------------------------------------------------------ */
615 /*
616  * Purpose:	Release a DOS-style lock
617  * Returns:	nlm_granted, unless in grace period
618  * Notes:
619  */
620 nlm_shareres *
621 nlm_unshare_3_svc(arg, rqstp)
622 	nlm_shareargs *arg;
623 	struct svc_req *rqstp;
624 {
625 	static nlm_shareres res;
626 
627 	if (debug_level)
628 		log_from_addr("nlm_unshare", rqstp);
629 
630 	res.cookie = arg->cookie;
631 	res.stat = nlm_granted;
632 	res.sequence = 1234356;	/* X/Open says this field is ignored? */
633 	return (&res);
634 }
635 
636 /* nlm_nm_lock ------------------------------------------------------------ */
637 /*
638  * Purpose:	non-monitored version of nlm_lock()
639  * Returns:	as for nlm_lock()
640  * Notes:	These locks are in the same style as the standard nlm_lock,
641  *		but the rpc.statd should not be called to establish a
642  *		monitor for the client machine, since that machine is
643  *		declared not to be running a rpc.statd, and so would not
644  *		respond to the statd protocol.
645  */
646 nlm_res *
647 nlm_nm_lock_3_svc(arg, rqstp)
648 	nlm_lockargs *arg;
649 	struct svc_req *rqstp;
650 {
651 	static nlm_res res;
652 
653 	if (debug_level)
654 		log_from_addr("nlm_nm_lock", rqstp);
655 
656 	/* copy cookie from arg to result.  See comment in nlm_test_1() */
657 	res.cookie = arg->cookie;
658 	res.stat.stat = nlm_granted;
659 	return (&res);
660 }
661 
662 /* nlm_free_all ------------------------------------------------------------ */
663 /*
664  * Purpose:	Release all locks held by a named client
665  * Returns:	Nothing
666  * Notes:	Potential denial of service security problem here - the
667  *		locks to be released are specified by a host name, independent
668  *		of the address from which the request has arrived.
669  *		Should probably be rejected if the named host has been
670  *		using monitored locks.
671  */
672 void *
673 nlm_free_all_3_svc(arg, rqstp)
674 	nlm_notify *arg;
675 	struct svc_req *rqstp;
676 {
677 	static char dummy;
678 
679 	if (debug_level)
680 		log_from_addr("nlm_free_all", rqstp);
681 	return (&dummy);
682 }
683