nlm_prot_impl.c revision 177633
1177633Sdfr/*- 2177633Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ 3177633Sdfr * Authors: Doug Rabson <dfr@rabson.org> 4177633Sdfr * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org> 5177633Sdfr * 6177633Sdfr * Redistribution and use in source and binary forms, with or without 7177633Sdfr * modification, are permitted provided that the following conditions 8177633Sdfr * are met: 9177633Sdfr * 1. Redistributions of source code must retain the above copyright 10177633Sdfr * notice, this list of conditions and the following disclaimer. 11177633Sdfr * 2. Redistributions in binary form must reproduce the above copyright 12177633Sdfr * notice, this list of conditions and the following disclaimer in the 13177633Sdfr * documentation and/or other materials provided with the distribution. 14177633Sdfr * 15177633Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16177633Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17177633Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18177633Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19177633Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20177633Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21177633Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22177633Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23177633Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24177633Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25177633Sdfr * SUCH DAMAGE. 26177633Sdfr */ 27177633Sdfr 28177633Sdfr#include "opt_inet6.h" 29177633Sdfr 30177633Sdfr#include <sys/cdefs.h> 31177633Sdfr__FBSDID("$FreeBSD: head/sys/nlm/nlm_prot_impl.c 177633 2008-03-26 15:23:12Z dfr $"); 32177633Sdfr 33177633Sdfr#include <sys/param.h> 34177633Sdfr#include <sys/fcntl.h> 35177633Sdfr#include <sys/kernel.h> 36177633Sdfr#include <sys/lockf.h> 37177633Sdfr#include <sys/malloc.h> 38177633Sdfr#include <sys/mount.h> 39177633Sdfr#include <sys/priv.h> 40177633Sdfr#include <sys/proc.h> 41177633Sdfr#include <sys/socket.h> 42177633Sdfr#include <sys/socketvar.h> 43177633Sdfr#include <sys/syscall.h> 44177633Sdfr#include <sys/sysctl.h> 45177633Sdfr#include <sys/sysent.h> 46177633Sdfr#include <sys/sysproto.h> 47177633Sdfr#include <sys/systm.h> 48177633Sdfr#include <sys/taskqueue.h> 49177633Sdfr#include <sys/unistd.h> 50177633Sdfr#include <sys/vnode.h> 51177633Sdfr 52177633Sdfr#include "nlm_prot.h" 53177633Sdfr#include "sm_inter.h" 54177633Sdfr#include "nlm.h" 55177633Sdfr#include <rpc/rpc_com.h> 56177633Sdfr#include <rpc/rpcb_prot.h> 57177633Sdfr 58177633SdfrMALLOC_DEFINE(M_NLM, "NLM", "Network Lock Manager"); 59177633Sdfr 60177633Sdfr/* 61177633Sdfr * If a host is inactive (and holds no locks) for this amount of 62177633Sdfr * seconds, we consider it idle and stop tracking it. 63177633Sdfr */ 64177633Sdfr#define NLM_IDLE_TIMEOUT 30 65177633Sdfr 66177633Sdfr/* 67177633Sdfr * We check the host list for idle every few seconds. 68177633Sdfr */ 69177633Sdfr#define NLM_IDLE_PERIOD 5 70177633Sdfr 71177633Sdfr/* 72177633Sdfr * Support for sysctl vfs.nlm.sysid 73177633Sdfr */ 74177633SdfrSYSCTL_NODE(_vfs, OID_AUTO, nlm, CTLFLAG_RW, NULL, "Network Lock Manager"); 75177633SdfrSYSCTL_NODE(_vfs_nlm, OID_AUTO, sysid, CTLFLAG_RW, NULL, ""); 76177633Sdfr 77177633Sdfr/* 78177633Sdfr * Syscall hooks 79177633Sdfr */ 80177633Sdfrstatic int nlm_syscall_offset = SYS_nlm_syscall; 81177633Sdfrstatic struct sysent nlm_syscall_prev_sysent; 82177633SdfrMAKE_SYSENT(nlm_syscall); 83177633Sdfrstatic bool_t nlm_syscall_registered = FALSE; 84177633Sdfr 85177633Sdfr/* 86177633Sdfr * Debug level passed in from userland. We also support a sysctl hook 87177633Sdfr * so that it can be changed on a live system. 88177633Sdfr */ 89177633Sdfrstatic int nlm_debug_level; 90177633SdfrSYSCTL_INT(_debug, OID_AUTO, nlm_debug, CTLFLAG_RW, &nlm_debug_level, 0, ""); 91177633Sdfr 92177633Sdfr/* 93177633Sdfr * Grace period handling. The value of nlm_grace_threshold is the 94177633Sdfr * value of time_uptime after which we are serving requests normally. 95177633Sdfr */ 96177633Sdfrstatic time_t nlm_grace_threshold; 97177633Sdfr 98177633Sdfr/* 99177633Sdfr * We check for idle hosts if time_uptime is greater than 100177633Sdfr * nlm_next_idle_check, 101177633Sdfr */ 102177633Sdfrstatic time_t nlm_next_idle_check; 103177633Sdfr 104177633Sdfr/* 105177633Sdfr * A socket to use for RPC - shared by all IPv4 RPC clients. 106177633Sdfr */ 107177633Sdfrstatic struct socket *nlm_socket; 108177633Sdfr 109177633Sdfr#ifdef INET6 110177633Sdfr 111177633Sdfr/* 112177633Sdfr * A socket to use for RPC - shared by all IPv6 RPC clients. 113177633Sdfr */ 114177633Sdfrstatic struct socket *nlm_socket6; 115177633Sdfr 116177633Sdfr#endif 117177633Sdfr 118177633Sdfr/* 119177633Sdfr * An RPC client handle that can be used to communicate with the local 120177633Sdfr * NSM. 121177633Sdfr */ 122177633Sdfrstatic CLIENT *nlm_nsm; 123177633Sdfr 124177633Sdfr/* 125177633Sdfr * An RPC client handle that can be used to communicate with the 126177633Sdfr * userland part of lockd. 127177633Sdfr */ 128177633Sdfrstatic CLIENT *nlm_lockd; 129177633Sdfr 130177633Sdfr/* 131177633Sdfr * Locks: 132177633Sdfr * (l) locked by nh_lock 133177633Sdfr * (s) only accessed via server RPC which is single threaded 134177633Sdfr * (c) const until freeing 135177633Sdfr */ 136177633Sdfr 137177633Sdfr/* 138177633Sdfr * A pending asynchronous lock request, stored on the nc_pending list 139177633Sdfr * of the NLM host. 140177633Sdfr */ 141177633Sdfrstruct nlm_async_lock { 142177633Sdfr TAILQ_ENTRY(nlm_async_lock) af_link; /* (l) host's list of locks */ 143177633Sdfr struct task af_task; /* (c) async callback details */ 144177633Sdfr void *af_cookie; /* (l) lock manager cancel token */ 145177633Sdfr struct vnode *af_vp; /* (l) vnode to lock */ 146177633Sdfr struct flock af_fl; /* (c) lock details */ 147177633Sdfr struct nlm_host *af_host; /* (c) host which is locking */ 148177633Sdfr nlm4_testargs af_granted; /* (c) notification details */ 149177633Sdfr}; 150177633SdfrTAILQ_HEAD(nlm_async_lock_list, nlm_async_lock); 151177633Sdfr 152177633Sdfr/* 153177633Sdfr * NLM host. 154177633Sdfr */ 155177633Sdfrstruct nlm_host { 156177633Sdfr struct mtx nh_lock; 157177633Sdfr TAILQ_ENTRY(nlm_host) nh_link; /* (s) global list of hosts */ 158177633Sdfr char *nh_caller_name; /* (c) printable name of host */ 159177633Sdfr uint32_t nh_sysid; /* (c) our allocaed system ID */ 160177633Sdfr char nh_sysid_string[10]; /* (c) string rep. of sysid */ 161177633Sdfr struct sockaddr_storage nh_addr; /* (s) remote address of host */ 162177633Sdfr CLIENT *nh_rpc; /* (s) RPC handle to send to host */ 163177633Sdfr rpcvers_t nh_vers; /* (s) NLM version of host */ 164177633Sdfr int nh_state; /* (s) last seen NSM state of host */ 165177633Sdfr bool_t nh_monitored; /* (s) TRUE if local NSM is monitoring */ 166177633Sdfr time_t nh_idle_timeout; /* (s) Time at which host is idle */ 167177633Sdfr struct sysctl_ctx_list nh_sysctl; /* (c) vfs.nlm.sysid nodes */ 168177633Sdfr struct nlm_async_lock_list nh_pending; /* (l) pending async locks */ 169177633Sdfr struct nlm_async_lock_list nh_finished; /* (l) finished async locks */ 170177633Sdfr}; 171177633SdfrTAILQ_HEAD(nlm_host_list, nlm_host); 172177633Sdfr 173177633Sdfrstatic struct nlm_host_list nlm_hosts; 174177633Sdfrstatic uint32_t nlm_next_sysid = 1; 175177633Sdfr 176177633Sdfrstatic void nlm_host_unmonitor(struct nlm_host *); 177177633Sdfr 178177633Sdfr/**********************************************************************/ 179177633Sdfr 180177633Sdfr/* 181177633Sdfr * Initialise NLM globals. 182177633Sdfr */ 183177633Sdfrstatic void 184177633Sdfrnlm_init(void *dummy) 185177633Sdfr{ 186177633Sdfr int error; 187177633Sdfr 188177633Sdfr TAILQ_INIT(&nlm_hosts); 189177633Sdfr 190177633Sdfr error = syscall_register(&nlm_syscall_offset, &nlm_syscall_sysent, 191177633Sdfr &nlm_syscall_prev_sysent); 192177633Sdfr if (error) 193177633Sdfr printf("Can't register NLM syscall\n"); 194177633Sdfr else 195177633Sdfr nlm_syscall_registered = TRUE; 196177633Sdfr} 197177633SdfrSYSINIT(nlm_init, SI_SUB_LOCK, SI_ORDER_FIRST, nlm_init, NULL); 198177633Sdfr 199177633Sdfrstatic void 200177633Sdfrnlm_uninit(void *dummy) 201177633Sdfr{ 202177633Sdfr 203177633Sdfr if (nlm_syscall_registered) 204177633Sdfr syscall_deregister(&nlm_syscall_offset, 205177633Sdfr &nlm_syscall_prev_sysent); 206177633Sdfr} 207177633SdfrSYSUNINIT(nlm_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, nlm_uninit, NULL); 208177633Sdfr 209177633Sdfr/* 210177633Sdfr * Copy a struct netobj. 211177633Sdfr */ 212177633Sdfrvoid 213177633Sdfrnlm_copy_netobj(struct netobj *dst, struct netobj *src, 214177633Sdfr struct malloc_type *type) 215177633Sdfr{ 216177633Sdfr 217177633Sdfr dst->n_len = src->n_len; 218177633Sdfr dst->n_bytes = malloc(src->n_len, type, M_WAITOK); 219177633Sdfr memcpy(dst->n_bytes, src->n_bytes, src->n_len); 220177633Sdfr} 221177633Sdfr 222177633Sdfr/* 223177633Sdfr * Create an RPC client handle for the given (address,prog,vers) 224177633Sdfr * triple using UDP. 225177633Sdfr */ 226177633Sdfrstatic CLIENT * 227177633Sdfrnlm_get_rpc(struct sockaddr *sa, rpcprog_t prog, rpcvers_t vers) 228177633Sdfr{ 229177633Sdfr const char *wchan = "nlmrcv"; 230177633Sdfr const char* protofmly; 231177633Sdfr struct sockaddr_storage ss; 232177633Sdfr struct socket *so; 233177633Sdfr CLIENT *rpcb; 234177633Sdfr struct timeval timo; 235177633Sdfr RPCB parms; 236177633Sdfr char *uaddr; 237177633Sdfr enum clnt_stat stat; 238177633Sdfr int rpcvers; 239177633Sdfr 240177633Sdfr /* 241177633Sdfr * First we need to contact the remote RPCBIND service to find 242177633Sdfr * the right port. 243177633Sdfr */ 244177633Sdfr memcpy(&ss, sa, sa->sa_len); 245177633Sdfr switch (ss.ss_family) { 246177633Sdfr case AF_INET: 247177633Sdfr ((struct sockaddr_in *)&ss)->sin_port = htons(111); 248177633Sdfr protofmly = "inet"; 249177633Sdfr so = nlm_socket; 250177633Sdfr break; 251177633Sdfr 252177633Sdfr#ifdef INET6 253177633Sdfr case AF_INET6: 254177633Sdfr ((struct sockaddr_in6 *)&ss)->sin6_port = htons(111); 255177633Sdfr protofmly = "inet6"; 256177633Sdfr so = nlm_socket6; 257177633Sdfr break; 258177633Sdfr#endif 259177633Sdfr 260177633Sdfr default: 261177633Sdfr /* 262177633Sdfr * Unsupported address family - fail. 263177633Sdfr */ 264177633Sdfr return (NULL); 265177633Sdfr } 266177633Sdfr 267177633Sdfr rpcb = clnt_dg_create(so, (struct sockaddr *)&ss, 268177633Sdfr RPCBPROG, RPCBVERS4, 0, 0); 269177633Sdfr if (!rpcb) 270177633Sdfr return (NULL); 271177633Sdfr 272177633Sdfr parms.r_prog = prog; 273177633Sdfr parms.r_vers = vers; 274177633Sdfr parms.r_netid = "udp"; 275177633Sdfr parms.r_addr = ""; 276177633Sdfr parms.r_owner = ""; 277177633Sdfr 278177633Sdfr /* 279177633Sdfr * Use the default timeout. 280177633Sdfr */ 281177633Sdfr timo.tv_sec = 25; 282177633Sdfr timo.tv_usec = 0; 283177633Sdfragain: 284177633Sdfr uaddr = NULL; 285177633Sdfr stat = CLNT_CALL(rpcb, (rpcprog_t) RPCBPROC_GETADDR, 286177633Sdfr (xdrproc_t) xdr_rpcb, &parms, 287177633Sdfr (xdrproc_t) xdr_wrapstring, &uaddr, timo); 288177633Sdfr if (stat == RPC_PROGVERSMISMATCH) { 289177633Sdfr /* 290177633Sdfr * Try RPCBIND version 3 if we haven't already. 291177633Sdfr * 292177633Sdfr * XXX fall back to portmap? 293177633Sdfr */ 294177633Sdfr CLNT_CONTROL(rpcb, CLGET_VERS, &rpcvers); 295177633Sdfr if (rpcvers == RPCBVERS4) { 296177633Sdfr rpcvers = RPCBVERS; 297177633Sdfr CLNT_CONTROL(rpcb, CLSET_VERS, &rpcvers); 298177633Sdfr goto again; 299177633Sdfr } 300177633Sdfr } 301177633Sdfr 302177633Sdfr if (stat == RPC_SUCCESS) { 303177633Sdfr /* 304177633Sdfr * We have a reply from the remote RPCBIND - turn it into an 305177633Sdfr * appropriate address and make a new client that can talk to 306177633Sdfr * the remote NLM. 307177633Sdfr * 308177633Sdfr * XXX fixup IPv6 scope ID. 309177633Sdfr */ 310177633Sdfr struct netbuf *a; 311177633Sdfr a = __rpc_uaddr2taddr_af(ss.ss_family, uaddr); 312177633Sdfr memcpy(&ss, a->buf, a->len); 313177633Sdfr free(a->buf, M_RPC); 314177633Sdfr free(a, M_RPC); 315177633Sdfr xdr_free((xdrproc_t) xdr_wrapstring, &uaddr); 316177633Sdfr } else if (stat == RPC_PROGVERSMISMATCH) { 317177633Sdfr /* 318177633Sdfr * Try portmap. 319177633Sdfr */ 320177633Sdfr struct pmap mapping; 321177633Sdfr u_short port; 322177633Sdfr 323177633Sdfr rpcvers = PMAPVERS; 324177633Sdfr CLNT_CONTROL(rpcb, CLSET_VERS, &rpcvers); 325177633Sdfr 326177633Sdfr 327177633Sdfr mapping.pm_prog = parms.r_prog; 328177633Sdfr mapping.pm_vers = parms.r_vers; 329177633Sdfr mapping.pm_prot = IPPROTO_UDP; 330177633Sdfr mapping.pm_port = 0; 331177633Sdfr 332177633Sdfr stat = CLNT_CALL(rpcb, (rpcprog_t) PMAPPROC_GETPORT, 333177633Sdfr (xdrproc_t) xdr_pmap, &mapping, 334177633Sdfr (xdrproc_t) xdr_u_short, &port, timo); 335177633Sdfr 336177633Sdfr if (stat == RPC_SUCCESS) { 337177633Sdfr switch (ss.ss_family) { 338177633Sdfr case AF_INET: 339177633Sdfr ((struct sockaddr_in *)&ss)->sin_port = 340177633Sdfr htons(port); 341177633Sdfr break; 342177633Sdfr 343177633Sdfr#ifdef INET6 344177633Sdfr case AF_INET6: 345177633Sdfr ((struct sockaddr_in6 *)&ss)->sin6_port = 346177633Sdfr htons(port); 347177633Sdfr break; 348177633Sdfr#endif 349177633Sdfr } 350177633Sdfr } 351177633Sdfr } 352177633Sdfr if (stat != RPC_SUCCESS) { 353177633Sdfr printf("NLM: failed to contact remote rpcbind, stat = %d\n", 354177633Sdfr (int) stat); 355177633Sdfr return (NULL); 356177633Sdfr } 357177633Sdfr 358177633Sdfr /* 359177633Sdfr * Re-use the client we used to speak to rpcbind. 360177633Sdfr */ 361177633Sdfr CLNT_CONTROL(rpcb, CLSET_SVC_ADDR, &ss); 362177633Sdfr CLNT_CONTROL(rpcb, CLSET_PROG, &prog); 363177633Sdfr CLNT_CONTROL(rpcb, CLSET_VERS, &vers); 364177633Sdfr CLNT_CONTROL(rpcb, CLSET_WAITCHAN, &wchan); 365177633Sdfr rpcb->cl_auth = authunix_create(curthread->td_ucred); 366177633Sdfr 367177633Sdfr return (rpcb); 368177633Sdfr} 369177633Sdfr 370177633Sdfr/* 371177633Sdfr * This async callback after when an async lock request has been 372177633Sdfr * granted. We notify the host which initiated the request. 373177633Sdfr */ 374177633Sdfrstatic void 375177633Sdfrnlm_lock_callback(void *arg, int pending) 376177633Sdfr{ 377177633Sdfr struct nlm_async_lock *af = (struct nlm_async_lock *) arg; 378177633Sdfr 379177633Sdfr if (nlm_debug_level >= 2) 380177633Sdfr printf("NLM: async lock %p for %s (sysid %d) granted\n", 381177633Sdfr af, af->af_host->nh_caller_name, 382177633Sdfr af->af_host->nh_sysid); 383177633Sdfr 384177633Sdfr /* 385177633Sdfr * Send the results back to the host. 386177633Sdfr * 387177633Sdfr * Note: there is a possible race here with nlm_host_notify 388177633Sdfr * destroying teh RPC client. To avoid problems, the first 389177633Sdfr * thing nlm_host_notify does is to cancel pending async lock 390177633Sdfr * requests. 391177633Sdfr */ 392177633Sdfr if (af->af_host->nh_vers == NLM_VERS4) { 393177633Sdfr nlm4_granted_msg_4(&af->af_granted, 394177633Sdfr NULL, af->af_host->nh_rpc); 395177633Sdfr } else { 396177633Sdfr /* 397177633Sdfr * Back-convert to legacy protocol 398177633Sdfr */ 399177633Sdfr nlm_testargs granted; 400177633Sdfr granted.cookie = af->af_granted.cookie; 401177633Sdfr granted.exclusive = af->af_granted.exclusive; 402177633Sdfr granted.alock.caller_name = 403177633Sdfr af->af_granted.alock.caller_name; 404177633Sdfr granted.alock.fh = af->af_granted.alock.fh; 405177633Sdfr granted.alock.oh = af->af_granted.alock.oh; 406177633Sdfr granted.alock.svid = af->af_granted.alock.svid; 407177633Sdfr granted.alock.l_offset = 408177633Sdfr af->af_granted.alock.l_offset; 409177633Sdfr granted.alock.l_len = 410177633Sdfr af->af_granted.alock.l_len; 411177633Sdfr 412177633Sdfr nlm_granted_msg_1(&granted, 413177633Sdfr NULL, af->af_host->nh_rpc); 414177633Sdfr } 415177633Sdfr 416177633Sdfr /* 417177633Sdfr * Move this entry to the nh_finished list. Someone else will 418177633Sdfr * free it later - its too hard to do it here safely without 419177633Sdfr * racing with cancel. 420177633Sdfr * 421177633Sdfr * XXX possibly we should have a third "granted sent but not 422177633Sdfr * ack'ed" list so that we can re-send the granted message. 423177633Sdfr */ 424177633Sdfr mtx_lock(&af->af_host->nh_lock); 425177633Sdfr TAILQ_REMOVE(&af->af_host->nh_pending, af, af_link); 426177633Sdfr TAILQ_INSERT_TAIL(&af->af_host->nh_finished, af, af_link); 427177633Sdfr mtx_unlock(&af->af_host->nh_lock); 428177633Sdfr} 429177633Sdfr 430177633Sdfr/* 431177633Sdfr * Free an async lock request. The request must have been removed from 432177633Sdfr * any list. 433177633Sdfr */ 434177633Sdfrstatic void 435177633Sdfrnlm_free_async_lock(struct nlm_async_lock *af) 436177633Sdfr{ 437177633Sdfr /* 438177633Sdfr * Free an async lock. 439177633Sdfr */ 440177633Sdfr xdr_free((xdrproc_t) xdr_nlm4_testargs, &af->af_granted); 441177633Sdfr if (af->af_vp) 442177633Sdfr vrele(af->af_vp); 443177633Sdfr free(af, M_NLM); 444177633Sdfr} 445177633Sdfr 446177633Sdfr/* 447177633Sdfr * Cancel our async request - this must be called with 448177633Sdfr * af->nh_host->nh_lock held. This is slightly complicated by a 449177633Sdfr * potential race with our own callback. If we fail to cancel the 450177633Sdfr * lock, it must already have been granted - we make sure our async 451177633Sdfr * task has completed by calling taskqueue_drain in this case. 452177633Sdfr */ 453177633Sdfrstatic int 454177633Sdfrnlm_cancel_async_lock(struct nlm_async_lock *af) 455177633Sdfr{ 456177633Sdfr struct nlm_host *host = af->af_host; 457177633Sdfr int error; 458177633Sdfr 459177633Sdfr mtx_assert(&host->nh_lock, MA_OWNED); 460177633Sdfr 461177633Sdfr mtx_unlock(&host->nh_lock); 462177633Sdfr 463177633Sdfr error = VOP_ADVLOCKASYNC(af->af_vp, NULL, F_CANCEL, &af->af_fl, 464177633Sdfr F_REMOTE, NULL, &af->af_cookie); 465177633Sdfr 466177633Sdfr if (error) { 467177633Sdfr /* 468177633Sdfr * We failed to cancel - make sure our callback has 469177633Sdfr * completed before we continue. 470177633Sdfr */ 471177633Sdfr taskqueue_drain(taskqueue_thread, &af->af_task); 472177633Sdfr } 473177633Sdfr 474177633Sdfr mtx_lock(&host->nh_lock); 475177633Sdfr 476177633Sdfr if (!error) { 477177633Sdfr if (nlm_debug_level >= 2) 478177633Sdfr printf("NLM: async lock %p for %s (sysid %d) " 479177633Sdfr "cancelled\n", 480177633Sdfr af, host->nh_caller_name, host->nh_sysid); 481177633Sdfr 482177633Sdfr /* 483177633Sdfr * Remove from the nh_pending list and free now that 484177633Sdfr * we are safe from the callback. 485177633Sdfr */ 486177633Sdfr TAILQ_REMOVE(&host->nh_pending, af, af_link); 487177633Sdfr mtx_unlock(&host->nh_lock); 488177633Sdfr nlm_free_async_lock(af); 489177633Sdfr mtx_lock(&host->nh_lock); 490177633Sdfr } 491177633Sdfr 492177633Sdfr return (error); 493177633Sdfr} 494177633Sdfr 495177633Sdfrstatic void 496177633Sdfrnlm_free_finished_locks(struct nlm_host *host) 497177633Sdfr{ 498177633Sdfr struct nlm_async_lock *af; 499177633Sdfr 500177633Sdfr mtx_lock(&host->nh_lock); 501177633Sdfr while ((af = TAILQ_FIRST(&host->nh_finished)) != NULL) { 502177633Sdfr TAILQ_REMOVE(&host->nh_finished, af, af_link); 503177633Sdfr mtx_unlock(&host->nh_lock); 504177633Sdfr nlm_free_async_lock(af); 505177633Sdfr mtx_lock(&host->nh_lock); 506177633Sdfr } 507177633Sdfr mtx_unlock(&host->nh_lock); 508177633Sdfr} 509177633Sdfr 510177633Sdfr/* 511177633Sdfr * This is called when we receive a host state change 512177633Sdfr * notification. We unlock any active locks owned by the host. 513177633Sdfr */ 514177633Sdfrstatic void 515177633Sdfrnlm_host_notify(struct nlm_host *host, int newstate, bool_t destroy) 516177633Sdfr{ 517177633Sdfr struct nlm_async_lock *af; 518177633Sdfr 519177633Sdfr if (newstate) { 520177633Sdfr if (nlm_debug_level >= 1) 521177633Sdfr printf("NLM: host %s (sysid %d) rebooted, new " 522177633Sdfr "state is %d\n", 523177633Sdfr host->nh_caller_name, host->nh_sysid, newstate); 524177633Sdfr } 525177633Sdfr 526177633Sdfr /* 527177633Sdfr * Cancel any pending async locks for this host. 528177633Sdfr */ 529177633Sdfr mtx_lock(&host->nh_lock); 530177633Sdfr while ((af = TAILQ_FIRST(&host->nh_pending)) != NULL) { 531177633Sdfr /* 532177633Sdfr * nlm_cancel_async_lock will remove the entry from 533177633Sdfr * nh_pending and free it. 534177633Sdfr */ 535177633Sdfr nlm_cancel_async_lock(af); 536177633Sdfr } 537177633Sdfr mtx_unlock(&host->nh_lock); 538177633Sdfr nlm_free_finished_locks(host); 539177633Sdfr 540177633Sdfr /* 541177633Sdfr * The host just rebooted - trash its locks and forget any 542177633Sdfr * RPC client handle that we may have for it. 543177633Sdfr */ 544177633Sdfr lf_clearremotesys(host->nh_sysid); 545177633Sdfr if (host->nh_rpc) { 546177633Sdfr AUTH_DESTROY(host->nh_rpc->cl_auth); 547177633Sdfr CLNT_DESTROY(host->nh_rpc); 548177633Sdfr host->nh_rpc = NULL; 549177633Sdfr } 550177633Sdfr host->nh_state = newstate; 551177633Sdfr 552177633Sdfr /* 553177633Sdfr * Destroy the host if the caller believes that it won't be 554177633Sdfr * used again. This is safe enough - if we see the same name 555177633Sdfr * again, we will just create a new host. 556177633Sdfr */ 557177633Sdfr if (destroy) { 558177633Sdfr TAILQ_REMOVE(&nlm_hosts, host, nh_link); 559177633Sdfr mtx_destroy(&host->nh_lock); 560177633Sdfr sysctl_ctx_free(&host->nh_sysctl); 561177633Sdfr free(host->nh_caller_name, M_NLM); 562177633Sdfr free(host, M_NLM); 563177633Sdfr } 564177633Sdfr} 565177633Sdfr 566177633Sdfr/* 567177633Sdfr * Sysctl handler to count the number of locks for a sysid. 568177633Sdfr */ 569177633Sdfrstatic int 570177633Sdfrnlm_host_lock_count_sysctl(SYSCTL_HANDLER_ARGS) 571177633Sdfr{ 572177633Sdfr struct nlm_host *host; 573177633Sdfr int count; 574177633Sdfr 575177633Sdfr host = oidp->oid_arg1; 576177633Sdfr count = lf_countlocks(host->nh_sysid); 577177633Sdfr return sysctl_handle_int(oidp, &count, 0, req); 578177633Sdfr} 579177633Sdfr 580177633Sdfr/* 581177633Sdfr * Create a new NLM host. 582177633Sdfr */ 583177633Sdfrstatic struct nlm_host * 584177633Sdfrnlm_create_host(const char* caller_name) 585177633Sdfr{ 586177633Sdfr struct nlm_host *host; 587177633Sdfr struct sysctl_oid *oid; 588177633Sdfr 589177633Sdfr if (nlm_debug_level >= 1) 590177633Sdfr printf("NLM: new host %s (sysid %d)\n", 591177633Sdfr caller_name, nlm_next_sysid); 592177633Sdfr host = malloc(sizeof(struct nlm_host), M_NLM, M_WAITOK|M_ZERO); 593177633Sdfr mtx_init(&host->nh_lock, "nh_lock", NULL, MTX_DEF); 594177633Sdfr host->nh_caller_name = strdup(caller_name, M_NLM); 595177633Sdfr host->nh_sysid = nlm_next_sysid++; 596177633Sdfr snprintf(host->nh_sysid_string, sizeof(host->nh_sysid_string), 597177633Sdfr "%d", host->nh_sysid); 598177633Sdfr host->nh_rpc = NULL; 599177633Sdfr host->nh_vers = 0; 600177633Sdfr host->nh_state = 0; 601177633Sdfr host->nh_monitored = FALSE; 602177633Sdfr TAILQ_INIT(&host->nh_pending); 603177633Sdfr TAILQ_INIT(&host->nh_finished); 604177633Sdfr TAILQ_INSERT_TAIL(&nlm_hosts, host, nh_link); 605177633Sdfr 606177633Sdfr sysctl_ctx_init(&host->nh_sysctl); 607177633Sdfr oid = SYSCTL_ADD_NODE(&host->nh_sysctl, 608177633Sdfr SYSCTL_STATIC_CHILDREN(_vfs_nlm_sysid), 609177633Sdfr OID_AUTO, host->nh_sysid_string, CTLFLAG_RD, NULL, ""); 610177633Sdfr SYSCTL_ADD_STRING(&host->nh_sysctl, SYSCTL_CHILDREN(oid), OID_AUTO, 611177633Sdfr "hostname", CTLFLAG_RD, host->nh_caller_name, 0, ""); 612177633Sdfr SYSCTL_ADD_INT(&host->nh_sysctl, SYSCTL_CHILDREN(oid), OID_AUTO, 613177633Sdfr "version", CTLFLAG_RD, &host->nh_vers, 0, ""); 614177633Sdfr SYSCTL_ADD_INT(&host->nh_sysctl, SYSCTL_CHILDREN(oid), OID_AUTO, 615177633Sdfr "monitored", CTLFLAG_RD, &host->nh_monitored, 0, ""); 616177633Sdfr SYSCTL_ADD_PROC(&host->nh_sysctl, SYSCTL_CHILDREN(oid), OID_AUTO, 617177633Sdfr "lock_count", CTLTYPE_INT | CTLFLAG_RD, host, 0, 618177633Sdfr nlm_host_lock_count_sysctl, "I", ""); 619177633Sdfr 620177633Sdfr return (host); 621177633Sdfr} 622177633Sdfr 623177633Sdfr/* 624177633Sdfr * Return non-zero if the address parts of the two sockaddrs are the 625177633Sdfr * same. 626177633Sdfr */ 627177633Sdfrstatic int 628177633Sdfrnlm_compare_addr(const struct sockaddr *a, const struct sockaddr *b) 629177633Sdfr{ 630177633Sdfr const struct sockaddr_in *a4, *b4; 631177633Sdfr#ifdef INET6 632177633Sdfr const struct sockaddr_in6 *a6, *b6; 633177633Sdfr#endif 634177633Sdfr 635177633Sdfr if (a->sa_family != b->sa_family) 636177633Sdfr return (FALSE); 637177633Sdfr 638177633Sdfr switch (a->sa_family) { 639177633Sdfr case AF_INET: 640177633Sdfr a4 = (const struct sockaddr_in *) a; 641177633Sdfr b4 = (const struct sockaddr_in *) b; 642177633Sdfr return !memcmp(&a4->sin_addr, &b4->sin_addr, 643177633Sdfr sizeof(a4->sin_addr)); 644177633Sdfr#ifdef INET6 645177633Sdfr case AF_INET6: 646177633Sdfr a6 = (const struct sockaddr_in6 *) a; 647177633Sdfr b6 = (const struct sockaddr_in6 *) b; 648177633Sdfr return !memcmp(&a6->sin6_addr, &b6->sin6_addr, 649177633Sdfr sizeof(a6->sin6_addr)); 650177633Sdfr#endif 651177633Sdfr } 652177633Sdfr 653177633Sdfr return (0); 654177633Sdfr} 655177633Sdfr 656177633Sdfr/* 657177633Sdfr * Check for idle hosts and stop monitoring them. We could also free 658177633Sdfr * the host structure here, possibly after a larger timeout but that 659177633Sdfr * would require some care to avoid races with 660177633Sdfr * e.g. nlm_host_lock_count_sysctl. 661177633Sdfr */ 662177633Sdfrstatic void 663177633Sdfrnlm_check_idle(void) 664177633Sdfr{ 665177633Sdfr struct nlm_host *host; 666177633Sdfr 667177633Sdfr if (time_uptime <= nlm_next_idle_check) 668177633Sdfr return; 669177633Sdfr 670177633Sdfr nlm_next_idle_check = time_uptime + NLM_IDLE_PERIOD; 671177633Sdfr 672177633Sdfr TAILQ_FOREACH(host, &nlm_hosts, nh_link) { 673177633Sdfr if (host->nh_monitored 674177633Sdfr && time_uptime > host->nh_idle_timeout) { 675177633Sdfr if (lf_countlocks(host->nh_sysid) > 0) { 676177633Sdfr host->nh_idle_timeout = 677177633Sdfr time_uptime + NLM_IDLE_TIMEOUT; 678177633Sdfr continue; 679177633Sdfr } 680177633Sdfr nlm_host_unmonitor(host); 681177633Sdfr } 682177633Sdfr } 683177633Sdfr} 684177633Sdfr 685177633Sdfr/* 686177633Sdfr * Search for an existing NLM host that matches the given name 687177633Sdfr * (typically the caller_name element of an nlm4_lock). If none is 688177633Sdfr * found, create a new host. If 'rqstp' is non-NULL, record the remote 689177633Sdfr * address of the host so that we can call it back for async 690177633Sdfr * responses. 691177633Sdfr */ 692177633Sdfrstruct nlm_host * 693177633Sdfrnlm_find_host_by_name(const char *name, struct svc_req *rqstp) 694177633Sdfr{ 695177633Sdfr struct nlm_host *host; 696177633Sdfr 697177633Sdfr nlm_check_idle(); 698177633Sdfr 699177633Sdfr /* 700177633Sdfr * The remote host is determined by caller_name. 701177633Sdfr */ 702177633Sdfr TAILQ_FOREACH(host, &nlm_hosts, nh_link) { 703177633Sdfr if (!strcmp(host->nh_caller_name, name)) 704177633Sdfr break; 705177633Sdfr } 706177633Sdfr 707177633Sdfr if (!host) 708177633Sdfr host = nlm_create_host(name); 709177633Sdfr host->nh_idle_timeout = time_uptime + NLM_IDLE_TIMEOUT; 710177633Sdfr 711177633Sdfr /* 712177633Sdfr * If we have an RPC request, record the remote address so 713177633Sdfr * that can send async replies etc. 714177633Sdfr */ 715177633Sdfr if (rqstp) { 716177633Sdfr struct netbuf *addr = &rqstp->rq_xprt->xp_rtaddr; 717177633Sdfr 718177633Sdfr KASSERT(addr->len < sizeof(struct sockaddr_storage), 719177633Sdfr ("Strange remote transport address length")); 720177633Sdfr 721177633Sdfr /* 722177633Sdfr * If we have seen an address before and we currently 723177633Sdfr * have an RPC client handle, make sure the address is 724177633Sdfr * the same, otherwise discard the client handle. 725177633Sdfr */ 726177633Sdfr if (host->nh_addr.ss_len && host->nh_rpc) { 727177633Sdfr if (!nlm_compare_addr( 728177633Sdfr (struct sockaddr *) &host->nh_addr, 729177633Sdfr (struct sockaddr *) addr->buf) 730177633Sdfr || host->nh_vers != rqstp->rq_vers) { 731177633Sdfr AUTH_DESTROY(host->nh_rpc->cl_auth); 732177633Sdfr CLNT_DESTROY(host->nh_rpc); 733177633Sdfr host->nh_rpc = NULL; 734177633Sdfr } 735177633Sdfr } 736177633Sdfr memcpy(&host->nh_addr, addr->buf, addr->len); 737177633Sdfr host->nh_vers = rqstp->rq_vers; 738177633Sdfr } 739177633Sdfr 740177633Sdfr return (host); 741177633Sdfr} 742177633Sdfr 743177633Sdfr/* 744177633Sdfr * Search for an existing NLM host that matches the given remote 745177633Sdfr * address. If none is found, create a new host with the requested 746177633Sdfr * address and remember 'vers' as the NLM protocol version to use for 747177633Sdfr * that host. 748177633Sdfr */ 749177633Sdfrstruct nlm_host * 750177633Sdfrnlm_find_host_by_addr(const struct sockaddr *addr, int vers) 751177633Sdfr{ 752177633Sdfr struct nlm_host *host; 753177633Sdfr 754177633Sdfr nlm_check_idle(); 755177633Sdfr 756177633Sdfr /* 757177633Sdfr * The remote host is determined by caller_name. 758177633Sdfr */ 759177633Sdfr TAILQ_FOREACH(host, &nlm_hosts, nh_link) { 760177633Sdfr if (nlm_compare_addr(addr, 761177633Sdfr (const struct sockaddr *) &host->nh_addr)) 762177633Sdfr break; 763177633Sdfr } 764177633Sdfr 765177633Sdfr if (!host) { 766177633Sdfr /* 767177633Sdfr * Fake up a name using inet_ntop. This buffer is 768177633Sdfr * large enough for an IPv6 address. 769177633Sdfr */ 770177633Sdfr char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; 771177633Sdfr switch (addr->sa_family) { 772177633Sdfr case AF_INET: 773177633Sdfr __rpc_inet_ntop(AF_INET, 774177633Sdfr &((const struct sockaddr_in *) addr)->sin_addr, 775177633Sdfr tmp, sizeof tmp); 776177633Sdfr break; 777177633Sdfr#ifdef INET6 778177633Sdfr case AF_INET6: 779177633Sdfr __rpc_inet_ntop(AF_INET6, 780177633Sdfr &((const struct sockaddr_in6 *) addr)->sin6_addr, 781177633Sdfr tmp, sizeof tmp); 782177633Sdfr break; 783177633Sdfr#endif 784177633Sdfr default: 785177633Sdfr strcmp(tmp, "<unknown>"); 786177633Sdfr } 787177633Sdfr host = nlm_create_host(tmp); 788177633Sdfr memcpy(&host->nh_addr, addr, addr->sa_len); 789177633Sdfr host->nh_vers = vers; 790177633Sdfr } 791177633Sdfr host->nh_idle_timeout = time_uptime + NLM_IDLE_TIMEOUT; 792177633Sdfr 793177633Sdfr return (host); 794177633Sdfr} 795177633Sdfr 796177633Sdfr/* 797177633Sdfr * Find the NLM host that matches the value of 'sysid'. If none 798177633Sdfr * exists, return NULL. 799177633Sdfr */ 800177633Sdfrstatic struct nlm_host * 801177633Sdfrnlm_find_host_by_sysid(int sysid) 802177633Sdfr{ 803177633Sdfr struct nlm_host *host; 804177633Sdfr 805177633Sdfr TAILQ_FOREACH(host, &nlm_hosts, nh_link) { 806177633Sdfr if (host->nh_sysid == sysid) 807177633Sdfr return (host); 808177633Sdfr } 809177633Sdfr 810177633Sdfr return (NULL); 811177633Sdfr} 812177633Sdfr 813177633Sdfr/* 814177633Sdfr * Unregister this NLM host with the local NSM due to idleness. 815177633Sdfr */ 816177633Sdfrstatic void 817177633Sdfrnlm_host_unmonitor(struct nlm_host *host) 818177633Sdfr{ 819177633Sdfr mon_id smmonid; 820177633Sdfr sm_stat_res smstat; 821177633Sdfr struct timeval timo; 822177633Sdfr enum clnt_stat stat; 823177633Sdfr 824177633Sdfr if (nlm_debug_level >= 1) 825177633Sdfr printf("NLM: unmonitoring %s (sysid %d)\n", 826177633Sdfr host->nh_caller_name, host->nh_sysid); 827177633Sdfr 828177633Sdfr /* 829177633Sdfr * We put our assigned system ID value in the priv field to 830177633Sdfr * make it simpler to find the host if we are notified of a 831177633Sdfr * host restart. 832177633Sdfr */ 833177633Sdfr smmonid.mon_name = host->nh_caller_name; 834177633Sdfr smmonid.my_id.my_name = "localhost"; 835177633Sdfr smmonid.my_id.my_prog = NLM_PROG; 836177633Sdfr smmonid.my_id.my_vers = NLM_SM; 837177633Sdfr smmonid.my_id.my_proc = NLM_SM_NOTIFY; 838177633Sdfr 839177633Sdfr timo.tv_sec = 25; 840177633Sdfr timo.tv_usec = 0; 841177633Sdfr stat = CLNT_CALL(nlm_nsm, SM_UNMON, 842177633Sdfr (xdrproc_t) xdr_mon, &smmonid, 843177633Sdfr (xdrproc_t) xdr_sm_stat, &smstat, timo); 844177633Sdfr 845177633Sdfr if (stat != RPC_SUCCESS) { 846177633Sdfr printf("Failed to contact local NSM - rpc error %d\n", stat); 847177633Sdfr return; 848177633Sdfr } 849177633Sdfr if (smstat.res_stat == stat_fail) { 850177633Sdfr printf("Local NSM refuses to unmonitor %s\n", 851177633Sdfr host->nh_caller_name); 852177633Sdfr return; 853177633Sdfr } 854177633Sdfr 855177633Sdfr host->nh_monitored = FALSE; 856177633Sdfr} 857177633Sdfr 858177633Sdfr/* 859177633Sdfr * Register this NLM host with the local NSM so that we can be 860177633Sdfr * notified if it reboots. 861177633Sdfr */ 862177633Sdfrstatic void 863177633Sdfrnlm_host_monitor(struct nlm_host *host, int state) 864177633Sdfr{ 865177633Sdfr mon smmon; 866177633Sdfr sm_stat_res smstat; 867177633Sdfr struct timeval timo; 868177633Sdfr enum clnt_stat stat; 869177633Sdfr 870177633Sdfr if (host->nh_state && state && host->nh_state != state) { 871177633Sdfr /* 872177633Sdfr * The host rebooted without telling us. Trash its 873177633Sdfr * locks. 874177633Sdfr */ 875177633Sdfr nlm_host_notify(host, state, FALSE); 876177633Sdfr } 877177633Sdfr 878177633Sdfr if (state && !host->nh_state) { 879177633Sdfr /* 880177633Sdfr * This is the first time we have seen an NSM state 881177633Sdfr * value for this host. We record it here to help 882177633Sdfr * detect host reboots. 883177633Sdfr */ 884177633Sdfr host->nh_state = state; 885177633Sdfr if (nlm_debug_level >= 1) 886177633Sdfr printf("NLM: host %s (sysid %d) has NSM state %d\n", 887177633Sdfr host->nh_caller_name, host->nh_sysid, state); 888177633Sdfr } 889177633Sdfr 890177633Sdfr if (host->nh_monitored) 891177633Sdfr return; 892177633Sdfr 893177633Sdfr if (nlm_debug_level >= 1) 894177633Sdfr printf("NLM: monitoring %s (sysid %d)\n", 895177633Sdfr host->nh_caller_name, host->nh_sysid); 896177633Sdfr 897177633Sdfr /* 898177633Sdfr * We put our assigned system ID value in the priv field to 899177633Sdfr * make it simpler to find the host if we are notified of a 900177633Sdfr * host restart. 901177633Sdfr */ 902177633Sdfr smmon.mon_id.mon_name = host->nh_caller_name; 903177633Sdfr smmon.mon_id.my_id.my_name = "localhost"; 904177633Sdfr smmon.mon_id.my_id.my_prog = NLM_PROG; 905177633Sdfr smmon.mon_id.my_id.my_vers = NLM_SM; 906177633Sdfr smmon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; 907177633Sdfr memcpy(smmon.priv, &host->nh_sysid, sizeof(host->nh_sysid)); 908177633Sdfr 909177633Sdfr timo.tv_sec = 25; 910177633Sdfr timo.tv_usec = 0; 911177633Sdfr stat = CLNT_CALL(nlm_nsm, SM_MON, 912177633Sdfr (xdrproc_t) xdr_mon, &smmon, 913177633Sdfr (xdrproc_t) xdr_sm_stat, &smstat, timo); 914177633Sdfr 915177633Sdfr if (stat != RPC_SUCCESS) { 916177633Sdfr printf("Failed to contact local NSM - rpc error %d\n", stat); 917177633Sdfr return; 918177633Sdfr } 919177633Sdfr if (smstat.res_stat == stat_fail) { 920177633Sdfr printf("Local NSM refuses to monitor %s\n", 921177633Sdfr host->nh_caller_name); 922177633Sdfr return; 923177633Sdfr } 924177633Sdfr 925177633Sdfr host->nh_monitored = TRUE; 926177633Sdfr} 927177633Sdfr 928177633Sdfr/* 929177633Sdfr * Return an RPC client handle that can be used to talk to the NLM 930177633Sdfr * running on the given host. 931177633Sdfr */ 932177633SdfrCLIENT * 933177633Sdfrnlm_host_get_rpc(struct nlm_host *host) 934177633Sdfr{ 935177633Sdfr struct timeval zero; 936177633Sdfr 937177633Sdfr if (host->nh_rpc) 938177633Sdfr return (host->nh_rpc); 939177633Sdfr 940177633Sdfr /* 941177633Sdfr * Set the send timeout to zero - we only use this rpc handle 942177633Sdfr * for sending async replies which have no return value. 943177633Sdfr */ 944177633Sdfr host->nh_rpc = nlm_get_rpc((struct sockaddr *)&host->nh_addr, 945177633Sdfr NLM_PROG, host->nh_vers); 946177633Sdfr 947177633Sdfr if (host->nh_rpc) { 948177633Sdfr zero.tv_sec = 0; 949177633Sdfr zero.tv_usec = 0; 950177633Sdfr CLNT_CONTROL(host->nh_rpc, CLSET_TIMEOUT, &zero); 951177633Sdfr 952177633Sdfr /* 953177633Sdfr * Monitor the host - if it reboots, the address of 954177633Sdfr * its NSM might change so we must discard our RPC 955177633Sdfr * handle. 956177633Sdfr */ 957177633Sdfr nlm_host_monitor(host, 0); 958177633Sdfr } 959177633Sdfr 960177633Sdfr return (host->nh_rpc); 961177633Sdfr} 962177633Sdfr 963177633Sdfr/**********************************************************************/ 964177633Sdfr 965177633Sdfr/* 966177633Sdfr * Syscall interface with userland. 967177633Sdfr */ 968177633Sdfr 969177633Sdfrextern void nlm_prog_0(struct svc_req *rqstp, SVCXPRT *transp); 970177633Sdfrextern void nlm_prog_1(struct svc_req *rqstp, SVCXPRT *transp); 971177633Sdfrextern void nlm_prog_3(struct svc_req *rqstp, SVCXPRT *transp); 972177633Sdfrextern void nlm_prog_4(struct svc_req *rqstp, SVCXPRT *transp); 973177633Sdfr 974177633Sdfrstatic int 975177633Sdfrnlm_register_services(SVCPOOL *pool, int addr_count, char **addrs) 976177633Sdfr{ 977177633Sdfr static rpcvers_t versions[] = { 978177633Sdfr NLM_SM, NLM_VERS, NLM_VERSX, NLM_VERS4 979177633Sdfr }; 980177633Sdfr static void (*dispatchers[])(struct svc_req *, SVCXPRT *) = { 981177633Sdfr nlm_prog_0, nlm_prog_1, nlm_prog_3, nlm_prog_4 982177633Sdfr }; 983177633Sdfr static const int version_count = sizeof(versions) / sizeof(versions[0]); 984177633Sdfr 985177633Sdfr SVCXPRT **xprts; 986177633Sdfr char netid[16]; 987177633Sdfr char uaddr[128]; 988177633Sdfr struct netconfig *nconf; 989177633Sdfr int i, j, error; 990177633Sdfr 991177633Sdfr if (!addr_count) { 992177633Sdfr printf("NLM: no service addresses given - can't start server"); 993177633Sdfr return (EINVAL); 994177633Sdfr } 995177633Sdfr 996177633Sdfr xprts = malloc(addr_count * sizeof(SVCXPRT *), M_NLM, M_WAITOK); 997177633Sdfr for (i = 0; i < version_count; i++) { 998177633Sdfr for (j = 0; j < addr_count; j++) { 999177633Sdfr /* 1000177633Sdfr * Create transports for the first version and 1001177633Sdfr * then just register everything else to the 1002177633Sdfr * same transports. 1003177633Sdfr */ 1004177633Sdfr if (i == 0) { 1005177633Sdfr char *up; 1006177633Sdfr 1007177633Sdfr error = copyin(&addrs[2*j], &up, 1008177633Sdfr sizeof(char*)); 1009177633Sdfr if (error) 1010177633Sdfr goto out; 1011177633Sdfr error = copyinstr(up, netid, sizeof(netid), 1012177633Sdfr NULL); 1013177633Sdfr if (error) 1014177633Sdfr goto out; 1015177633Sdfr error = copyin(&addrs[2*j+1], &up, 1016177633Sdfr sizeof(char*)); 1017177633Sdfr if (error) 1018177633Sdfr goto out; 1019177633Sdfr error = copyinstr(up, uaddr, sizeof(uaddr), 1020177633Sdfr NULL); 1021177633Sdfr if (error) 1022177633Sdfr goto out; 1023177633Sdfr nconf = getnetconfigent(netid); 1024177633Sdfr if (!nconf) { 1025177633Sdfr printf("Can't lookup netid %s\n", 1026177633Sdfr netid); 1027177633Sdfr error = EINVAL; 1028177633Sdfr goto out; 1029177633Sdfr } 1030177633Sdfr xprts[j] = svc_tp_create(pool, dispatchers[i], 1031177633Sdfr NLM_PROG, versions[i], uaddr, nconf); 1032177633Sdfr if (!xprts[j]) { 1033177633Sdfr printf("NLM: unable to create " 1034177633Sdfr "(NLM_PROG, %d).\n", versions[i]); 1035177633Sdfr error = EINVAL; 1036177633Sdfr goto out; 1037177633Sdfr } 1038177633Sdfr freenetconfigent(nconf); 1039177633Sdfr } else { 1040177633Sdfr nconf = getnetconfigent(xprts[j]->xp_netid); 1041177633Sdfr rpcb_unset(NLM_PROG, versions[i], nconf); 1042177633Sdfr if (!svc_reg(xprts[j], NLM_PROG, versions[i], 1043177633Sdfr dispatchers[i], nconf)) { 1044177633Sdfr printf("NLM: can't register " 1045177633Sdfr "(NLM_PROG, %d)\n", versions[i]); 1046177633Sdfr error = EINVAL; 1047177633Sdfr goto out; 1048177633Sdfr } 1049177633Sdfr } 1050177633Sdfr } 1051177633Sdfr } 1052177633Sdfr error = 0; 1053177633Sdfrout: 1054177633Sdfr free(xprts, M_NLM); 1055177633Sdfr return (error); 1056177633Sdfr} 1057177633Sdfr 1058177633Sdfr/* 1059177633Sdfr * Main server entry point. Contacts the local NSM to get its current 1060177633Sdfr * state and send SM_UNMON_ALL. Registers the NLM services and then 1061177633Sdfr * services requests. Does not return until the server is interrupted 1062177633Sdfr * by a signal. 1063177633Sdfr */ 1064177633Sdfrstatic int 1065177633Sdfrnlm_server_main(int addr_count, char **addrs) 1066177633Sdfr{ 1067177633Sdfr struct thread *td = curthread; 1068177633Sdfr int error; 1069177633Sdfr SVCPOOL *pool; 1070177633Sdfr struct sockopt opt; 1071177633Sdfr int portlow; 1072177633Sdfr#ifdef INET6 1073177633Sdfr struct sockaddr_in6 sin6; 1074177633Sdfr#endif 1075177633Sdfr struct sockaddr_in sin; 1076177633Sdfr my_id id; 1077177633Sdfr sm_stat smstat; 1078177633Sdfr struct timeval timo; 1079177633Sdfr enum clnt_stat stat; 1080177633Sdfr struct nlm_host *host; 1081177633Sdfr 1082177633Sdfr if (nlm_socket) { 1083177633Sdfr printf("NLM: can't start server - it appears to be running already\n"); 1084177633Sdfr return (EPERM); 1085177633Sdfr } 1086177633Sdfr 1087177633Sdfr memset(&opt, 0, sizeof(opt)); 1088177633Sdfr 1089177633Sdfr nlm_socket = NULL; 1090177633Sdfr error = socreate(AF_INET, &nlm_socket, SOCK_DGRAM, 0, 1091177633Sdfr td->td_ucred, td); 1092177633Sdfr if (error) { 1093177633Sdfr printf("NLM: can't create IPv4 socket - error %d\n", error); 1094177633Sdfr return (error); 1095177633Sdfr } 1096177633Sdfr opt.sopt_dir = SOPT_SET; 1097177633Sdfr opt.sopt_level = IPPROTO_IP; 1098177633Sdfr opt.sopt_name = IP_PORTRANGE; 1099177633Sdfr portlow = IP_PORTRANGE_LOW; 1100177633Sdfr opt.sopt_val = &portlow; 1101177633Sdfr opt.sopt_valsize = sizeof(portlow); 1102177633Sdfr sosetopt(nlm_socket, &opt); 1103177633Sdfr 1104177633Sdfr#ifdef INET6 1105177633Sdfr nlm_socket6 = NULL; 1106177633Sdfr error = socreate(AF_INET6, &nlm_socket6, SOCK_DGRAM, 0, 1107177633Sdfr td->td_ucred, td); 1108177633Sdfr if (error) { 1109177633Sdfr printf("NLM: can't create IPv6 socket - error %d\n", error); 1110177633Sdfr return (error); 1111177633Sdfr } 1112177633Sdfr opt.sopt_dir = SOPT_SET; 1113177633Sdfr opt.sopt_level = IPPROTO_IPV6; 1114177633Sdfr opt.sopt_name = IPV6_PORTRANGE; 1115177633Sdfr portlow = IPV6_PORTRANGE_LOW; 1116177633Sdfr opt.sopt_val = &portlow; 1117177633Sdfr opt.sopt_valsize = sizeof(portlow); 1118177633Sdfr sosetopt(nlm_socket6, &opt); 1119177633Sdfr#endif 1120177633Sdfr 1121177633Sdfr#ifdef INET6 1122177633Sdfr memset(&sin6, 0, sizeof(sin6)); 1123177633Sdfr sin6.sin6_len = sizeof(sin6); 1124177633Sdfr sin6.sin6_family = AF_INET6; 1125177633Sdfr sin6.sin6_addr = in6addr_loopback; 1126177633Sdfr nlm_nsm = nlm_get_rpc((struct sockaddr *) &sin6, SM_PROG, SM_VERS); 1127177633Sdfr if (!nlm_nsm) { 1128177633Sdfr#endif 1129177633Sdfr memset(&sin, 0, sizeof(sin)); 1130177633Sdfr sin.sin_len = sizeof(sin); 1131177633Sdfr sin.sin_family = AF_INET6; 1132177633Sdfr sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1133177633Sdfr nlm_nsm = nlm_get_rpc((struct sockaddr *) &sin, SM_PROG, 1134177633Sdfr SM_VERS); 1135177633Sdfr#ifdef INET6 1136177633Sdfr } 1137177633Sdfr#endif 1138177633Sdfr 1139177633Sdfr if (!nlm_nsm) { 1140177633Sdfr printf("Can't start NLM - unable to contact NSM\n"); 1141177633Sdfr return (EINVAL); 1142177633Sdfr } 1143177633Sdfr 1144177633Sdfr pool = svcpool_create(); 1145177633Sdfr 1146177633Sdfr error = nlm_register_services(pool, addr_count, addrs); 1147177633Sdfr if (error) 1148177633Sdfr goto out; 1149177633Sdfr 1150177633Sdfr memset(&id, 0, sizeof(id)); 1151177633Sdfr id.my_name = "NFS NLM"; 1152177633Sdfr 1153177633Sdfr timo.tv_sec = 25; 1154177633Sdfr timo.tv_usec = 0; 1155177633Sdfr stat = CLNT_CALL(nlm_nsm, SM_UNMON_ALL, 1156177633Sdfr (xdrproc_t) xdr_my_id, &id, 1157177633Sdfr (xdrproc_t) xdr_sm_stat, &smstat, timo); 1158177633Sdfr 1159177633Sdfr if (stat != RPC_SUCCESS) { 1160177633Sdfr struct rpc_err err; 1161177633Sdfr 1162177633Sdfr CLNT_GETERR(nlm_nsm, &err); 1163177633Sdfr printf("NLM: unexpected error contacting NSM, stat=%d, errno=%d\n", 1164177633Sdfr stat, err.re_errno); 1165177633Sdfr error = EINVAL; 1166177633Sdfr goto out; 1167177633Sdfr } 1168177633Sdfr 1169177633Sdfr if (nlm_debug_level >= 1) 1170177633Sdfr printf("NLM: local NSM state is %d\n", smstat.state); 1171177633Sdfr 1172177633Sdfr svc_run(pool); 1173177633Sdfr error = 0; 1174177633Sdfr 1175177633Sdfrout: 1176177633Sdfr if (pool) 1177177633Sdfr svcpool_destroy(pool); 1178177633Sdfr 1179177633Sdfr /* 1180177633Sdfr * Trash all the existing state so that if the server 1181177633Sdfr * restarts, it gets a clean slate. 1182177633Sdfr */ 1183177633Sdfr while ((host = TAILQ_FIRST(&nlm_hosts)) != NULL) { 1184177633Sdfr nlm_host_notify(host, 0, TRUE); 1185177633Sdfr } 1186177633Sdfr if (nlm_nsm) { 1187177633Sdfr AUTH_DESTROY(nlm_nsm->cl_auth); 1188177633Sdfr CLNT_DESTROY(nlm_nsm); 1189177633Sdfr nlm_nsm = NULL; 1190177633Sdfr } 1191177633Sdfr if (nlm_lockd) { 1192177633Sdfr AUTH_DESTROY(nlm_lockd->cl_auth); 1193177633Sdfr CLNT_DESTROY(nlm_lockd); 1194177633Sdfr nlm_lockd = NULL; 1195177633Sdfr } 1196177633Sdfr 1197177633Sdfr soclose(nlm_socket); 1198177633Sdfr nlm_socket = NULL; 1199177633Sdfr#ifdef INET6 1200177633Sdfr soclose(nlm_socket6); 1201177633Sdfr nlm_socket6 = NULL; 1202177633Sdfr#endif 1203177633Sdfr 1204177633Sdfr return (error); 1205177633Sdfr} 1206177633Sdfr 1207177633Sdfrint 1208177633Sdfrnlm_syscall(struct thread *td, struct nlm_syscall_args *uap) 1209177633Sdfr{ 1210177633Sdfr int error; 1211177633Sdfr 1212177633Sdfr error = priv_check(td, PRIV_NFS_LOCKD); 1213177633Sdfr if (error) 1214177633Sdfr return (error); 1215177633Sdfr 1216177633Sdfr nlm_debug_level = uap->debug_level; 1217177633Sdfr nlm_grace_threshold = time_uptime + uap->grace_period; 1218177633Sdfr nlm_next_idle_check = time_uptime + NLM_IDLE_PERIOD; 1219177633Sdfr 1220177633Sdfr return nlm_server_main(uap->addr_count, uap->addrs); 1221177633Sdfr} 1222177633Sdfr 1223177633Sdfr/**********************************************************************/ 1224177633Sdfr 1225177633Sdfr/* 1226177633Sdfr * NLM implementation details, called from the RPC stubs. 1227177633Sdfr */ 1228177633Sdfr 1229177633Sdfr 1230177633Sdfrvoid 1231177633Sdfrnlm_sm_notify(struct nlm_sm_status *argp) 1232177633Sdfr{ 1233177633Sdfr uint32_t sysid; 1234177633Sdfr struct nlm_host *host; 1235177633Sdfr 1236177633Sdfr if (nlm_debug_level >= 3) 1237177633Sdfr printf("nlm_sm_notify(): mon_name = %s\n", argp->mon_name); 1238177633Sdfr memcpy(&sysid, &argp->priv, sizeof(sysid)); 1239177633Sdfr host = nlm_find_host_by_sysid(sysid); 1240177633Sdfr if (host) 1241177633Sdfr nlm_host_notify(host, argp->state, FALSE); 1242177633Sdfr} 1243177633Sdfr 1244177633Sdfrstatic void 1245177633Sdfrnlm_convert_to_fhandle_t(fhandle_t *fhp, struct netobj *p) 1246177633Sdfr{ 1247177633Sdfr memcpy(fhp, p->n_bytes, sizeof(fhandle_t)); 1248177633Sdfr} 1249177633Sdfr 1250177633Sdfrstruct vfs_state { 1251177633Sdfr struct mount *vs_mp; 1252177633Sdfr struct vnode *vs_vp; 1253177633Sdfr int vs_vfslocked; 1254177633Sdfr}; 1255177633Sdfr 1256177633Sdfrstatic int 1257177633Sdfrnlm_get_vfs_state(struct nlm_host *host, struct svc_req *rqstp, 1258177633Sdfr fhandle_t *fhp, struct vfs_state *vs) 1259177633Sdfr{ 1260177633Sdfr int error, exflags, freecred; 1261177633Sdfr struct ucred *cred = NULL, *credanon; 1262177633Sdfr 1263177633Sdfr memset(vs, 0, sizeof(*vs)); 1264177633Sdfr freecred = FALSE; 1265177633Sdfr 1266177633Sdfr vs->vs_mp = vfs_getvfs(&fhp->fh_fsid); 1267177633Sdfr if (!vs->vs_mp) { 1268177633Sdfr return (ESTALE); 1269177633Sdfr } 1270177633Sdfr vs->vs_vfslocked = VFS_LOCK_GIANT(vs->vs_mp); 1271177633Sdfr 1272177633Sdfr error = VFS_CHECKEXP(vs->vs_mp, (struct sockaddr *)&host->nh_addr, 1273177633Sdfr &exflags, &credanon); 1274177633Sdfr if (error) 1275177633Sdfr goto out; 1276177633Sdfr 1277177633Sdfr if (exflags & MNT_EXRDONLY || (vs->vs_mp->mnt_flag & MNT_RDONLY)) { 1278177633Sdfr error = EROFS; 1279177633Sdfr goto out; 1280177633Sdfr } 1281177633Sdfr 1282177633Sdfr error = VFS_FHTOVP(vs->vs_mp, &fhp->fh_fid, &vs->vs_vp); 1283177633Sdfr if (error) 1284177633Sdfr goto out; 1285177633Sdfr 1286177633Sdfr cred = crget(); 1287177633Sdfr freecred = TRUE; 1288177633Sdfr if (!svc_getcred(rqstp, cred, NULL)) { 1289177633Sdfr error = EINVAL; 1290177633Sdfr goto out; 1291177633Sdfr } 1292177633Sdfr if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) { 1293177633Sdfr crfree(cred); 1294177633Sdfr cred = credanon; 1295177633Sdfr freecred = FALSE; 1296177633Sdfr } 1297177633Sdfr#if __FreeBSD_version < 800011 1298177633Sdfr VOP_UNLOCK(vs->vs_vp, 0, curthread); 1299177633Sdfr#else 1300177633Sdfr VOP_UNLOCK(vs->vs_vp, 0); 1301177633Sdfr#endif 1302177633Sdfr 1303177633Sdfr /* 1304177633Sdfr * Check cred. 1305177633Sdfr */ 1306177633Sdfr error = VOP_ACCESS(vs->vs_vp, VWRITE, cred, curthread); 1307177633Sdfr if (error) 1308177633Sdfr goto out; 1309177633Sdfr 1310177633Sdfrout: 1311177633Sdfr if (freecred) 1312177633Sdfr crfree(cred); 1313177633Sdfr 1314177633Sdfr return (error); 1315177633Sdfr} 1316177633Sdfr 1317177633Sdfrstatic void 1318177633Sdfrnlm_release_vfs_state(struct vfs_state *vs) 1319177633Sdfr{ 1320177633Sdfr 1321177633Sdfr if (vs->vs_vp) 1322177633Sdfr vrele(vs->vs_vp); 1323177633Sdfr if (vs->vs_mp) 1324177633Sdfr vfs_rel(vs->vs_mp); 1325177633Sdfr VFS_UNLOCK_GIANT(vs->vs_vfslocked); 1326177633Sdfr} 1327177633Sdfr 1328177633Sdfrstatic nlm4_stats 1329177633Sdfrnlm_convert_error(int error) 1330177633Sdfr{ 1331177633Sdfr 1332177633Sdfr if (error == ESTALE) 1333177633Sdfr return nlm4_stale_fh; 1334177633Sdfr else if (error == EROFS) 1335177633Sdfr return nlm4_rofs; 1336177633Sdfr else 1337177633Sdfr return nlm4_failed; 1338177633Sdfr} 1339177633Sdfr 1340177633Sdfrstruct nlm_host * 1341177633Sdfrnlm_do_test(nlm4_testargs *argp, nlm4_testres *result, struct svc_req *rqstp) 1342177633Sdfr{ 1343177633Sdfr fhandle_t fh; 1344177633Sdfr struct vfs_state vs; 1345177633Sdfr struct nlm_host *host, *bhost; 1346177633Sdfr int error, sysid; 1347177633Sdfr struct flock fl; 1348177633Sdfr 1349177633Sdfr memset(result, 0, sizeof(*result)); 1350177633Sdfr 1351177633Sdfr host = nlm_find_host_by_name(argp->alock.caller_name, rqstp); 1352177633Sdfr if (!host) { 1353177633Sdfr result->stat.stat = nlm4_denied_nolocks; 1354177633Sdfr return (NULL); 1355177633Sdfr } 1356177633Sdfr 1357177633Sdfr if (nlm_debug_level >= 3) 1358177633Sdfr printf("nlm_do_test(): caller_name = %s (sysid = %d)\n", 1359177633Sdfr host->nh_caller_name, host->nh_sysid); 1360177633Sdfr 1361177633Sdfr nlm_free_finished_locks(host); 1362177633Sdfr sysid = host->nh_sysid; 1363177633Sdfr 1364177633Sdfr nlm_convert_to_fhandle_t(&fh, &argp->alock.fh); 1365177633Sdfr nlm_copy_netobj(&result->cookie, &argp->cookie, M_RPC); 1366177633Sdfr 1367177633Sdfr if (time_uptime < nlm_grace_threshold) { 1368177633Sdfr result->stat.stat = nlm4_denied_grace_period; 1369177633Sdfr return (host); 1370177633Sdfr } 1371177633Sdfr 1372177633Sdfr error = nlm_get_vfs_state(host, rqstp, &fh, &vs); 1373177633Sdfr if (error) { 1374177633Sdfr result->stat.stat = nlm_convert_error(error); 1375177633Sdfr goto out; 1376177633Sdfr } 1377177633Sdfr 1378177633Sdfr fl.l_start = argp->alock.l_offset; 1379177633Sdfr fl.l_len = argp->alock.l_len; 1380177633Sdfr fl.l_pid = argp->alock.svid; 1381177633Sdfr fl.l_sysid = sysid; 1382177633Sdfr fl.l_whence = SEEK_SET; 1383177633Sdfr if (argp->exclusive) 1384177633Sdfr fl.l_type = F_WRLCK; 1385177633Sdfr else 1386177633Sdfr fl.l_type = F_RDLCK; 1387177633Sdfr error = VOP_ADVLOCK(vs.vs_vp, NULL, F_GETLK, &fl, F_REMOTE); 1388177633Sdfr if (error) { 1389177633Sdfr result->stat.stat = nlm4_failed; 1390177633Sdfr goto out; 1391177633Sdfr } 1392177633Sdfr 1393177633Sdfr if (fl.l_type == F_UNLCK) { 1394177633Sdfr result->stat.stat = nlm4_granted; 1395177633Sdfr } else { 1396177633Sdfr result->stat.stat = nlm4_denied; 1397177633Sdfr result->stat.nlm4_testrply_u.holder.exclusive = 1398177633Sdfr (fl.l_type == F_WRLCK); 1399177633Sdfr result->stat.nlm4_testrply_u.holder.svid = fl.l_pid; 1400177633Sdfr bhost = nlm_find_host_by_sysid(fl.l_sysid); 1401177633Sdfr if (bhost) { 1402177633Sdfr /* 1403177633Sdfr * We don't have any useful way of recording 1404177633Sdfr * the value of oh used in the original lock 1405177633Sdfr * request. Ideally, the test reply would have 1406177633Sdfr * a space for the owning host's name allowing 1407177633Sdfr * our caller's NLM to keep track. 1408177633Sdfr * 1409177633Sdfr * As far as I can see, Solaris uses an eight 1410177633Sdfr * byte structure for oh which contains a four 1411177633Sdfr * byte pid encoded in local byte order and 1412177633Sdfr * the first four bytes of the host 1413177633Sdfr * name. Linux uses a variable length string 1414177633Sdfr * 'pid@hostname' in ascii but doesn't even 1415177633Sdfr * return that in test replies. 1416177633Sdfr * 1417177633Sdfr * For the moment, return nothing in oh 1418177633Sdfr * (already zero'ed above). 1419177633Sdfr */ 1420177633Sdfr } 1421177633Sdfr result->stat.nlm4_testrply_u.holder.l_offset = fl.l_start; 1422177633Sdfr result->stat.nlm4_testrply_u.holder.l_len = fl.l_len; 1423177633Sdfr } 1424177633Sdfr 1425177633Sdfrout: 1426177633Sdfr nlm_release_vfs_state(&vs); 1427177633Sdfr return (host); 1428177633Sdfr} 1429177633Sdfr 1430177633Sdfrstruct nlm_host * 1431177633Sdfrnlm_do_lock(nlm4_lockargs *argp, nlm4_res *result, struct svc_req *rqstp, 1432177633Sdfr bool_t monitor) 1433177633Sdfr{ 1434177633Sdfr fhandle_t fh; 1435177633Sdfr struct vfs_state vs; 1436177633Sdfr struct nlm_host *host; 1437177633Sdfr int error, sysid; 1438177633Sdfr struct flock fl; 1439177633Sdfr 1440177633Sdfr memset(result, 0, sizeof(*result)); 1441177633Sdfr 1442177633Sdfr host = nlm_find_host_by_name(argp->alock.caller_name, rqstp); 1443177633Sdfr if (!host) { 1444177633Sdfr result->stat.stat = nlm4_denied_nolocks; 1445177633Sdfr return (NULL); 1446177633Sdfr } 1447177633Sdfr 1448177633Sdfr if (nlm_debug_level >= 3) 1449177633Sdfr printf("nlm_do_lock(): caller_name = %s (sysid = %d)\n", 1450177633Sdfr host->nh_caller_name, host->nh_sysid); 1451177633Sdfr 1452177633Sdfr nlm_free_finished_locks(host); 1453177633Sdfr sysid = host->nh_sysid; 1454177633Sdfr 1455177633Sdfr nlm_convert_to_fhandle_t(&fh, &argp->alock.fh); 1456177633Sdfr nlm_copy_netobj(&result->cookie, &argp->cookie, M_RPC); 1457177633Sdfr 1458177633Sdfr if (time_uptime < nlm_grace_threshold && !argp->reclaim) { 1459177633Sdfr result->stat.stat = nlm4_denied_grace_period; 1460177633Sdfr return (host); 1461177633Sdfr } 1462177633Sdfr 1463177633Sdfr error = nlm_get_vfs_state(host, rqstp, &fh, &vs); 1464177633Sdfr if (error) { 1465177633Sdfr result->stat.stat = nlm_convert_error(error); 1466177633Sdfr goto out; 1467177633Sdfr } 1468177633Sdfr 1469177633Sdfr fl.l_start = argp->alock.l_offset; 1470177633Sdfr fl.l_len = argp->alock.l_len; 1471177633Sdfr fl.l_pid = argp->alock.svid; 1472177633Sdfr fl.l_sysid = sysid; 1473177633Sdfr fl.l_whence = SEEK_SET; 1474177633Sdfr if (argp->exclusive) 1475177633Sdfr fl.l_type = F_WRLCK; 1476177633Sdfr else 1477177633Sdfr fl.l_type = F_RDLCK; 1478177633Sdfr if (argp->block) { 1479177633Sdfr struct nlm_async_lock *af; 1480177633Sdfr 1481177633Sdfr /* 1482177633Sdfr * First, make sure we can contact the host's NLM. 1483177633Sdfr */ 1484177633Sdfr if (!nlm_host_get_rpc(host)) { 1485177633Sdfr result->stat.stat = nlm4_failed; 1486177633Sdfr goto out; 1487177633Sdfr } 1488177633Sdfr 1489177633Sdfr /* 1490177633Sdfr * First we need to check and see if there is an 1491177633Sdfr * existing blocked lock that matches. This could be a 1492177633Sdfr * badly behaved client or an RPC re-send. If we find 1493177633Sdfr * one, just return nlm4_blocked. 1494177633Sdfr */ 1495177633Sdfr mtx_lock(&host->nh_lock); 1496177633Sdfr TAILQ_FOREACH(af, &host->nh_pending, af_link) { 1497177633Sdfr if (af->af_fl.l_start == fl.l_start 1498177633Sdfr && af->af_fl.l_len == fl.l_len 1499177633Sdfr && af->af_fl.l_pid == fl.l_pid 1500177633Sdfr && af->af_fl.l_type == fl.l_type) { 1501177633Sdfr break; 1502177633Sdfr } 1503177633Sdfr } 1504177633Sdfr mtx_unlock(&host->nh_lock); 1505177633Sdfr if (af) { 1506177633Sdfr result->stat.stat = nlm4_blocked; 1507177633Sdfr goto out; 1508177633Sdfr } 1509177633Sdfr 1510177633Sdfr af = malloc(sizeof(struct nlm_async_lock), M_NLM, 1511177633Sdfr M_WAITOK|M_ZERO); 1512177633Sdfr TASK_INIT(&af->af_task, 0, nlm_lock_callback, af); 1513177633Sdfr af->af_vp = vs.vs_vp; 1514177633Sdfr af->af_fl = fl; 1515177633Sdfr af->af_host = host; 1516177633Sdfr /* 1517177633Sdfr * We use M_RPC here so that we can xdr_free the thing 1518177633Sdfr * later. 1519177633Sdfr */ 1520177633Sdfr af->af_granted.exclusive = argp->exclusive; 1521177633Sdfr af->af_granted.alock.caller_name = 1522177633Sdfr strdup(argp->alock.caller_name, M_RPC); 1523177633Sdfr nlm_copy_netobj(&af->af_granted.alock.fh, 1524177633Sdfr &argp->alock.fh, M_RPC); 1525177633Sdfr nlm_copy_netobj(&af->af_granted.alock.oh, 1526177633Sdfr &argp->alock.oh, M_RPC); 1527177633Sdfr af->af_granted.alock.svid = argp->alock.svid; 1528177633Sdfr af->af_granted.alock.l_offset = argp->alock.l_offset; 1529177633Sdfr af->af_granted.alock.l_len = argp->alock.l_len; 1530177633Sdfr 1531177633Sdfr /* 1532177633Sdfr * Put the entry on the pending list before calling 1533177633Sdfr * VOP_ADVLOCKASYNC. We do this in case the lock 1534177633Sdfr * request was blocked (returning EINPROGRESS) but 1535177633Sdfr * then granted before we manage to run again. The 1536177633Sdfr * client may receive the granted message before we 1537177633Sdfr * send our blocked reply but thats their problem. 1538177633Sdfr */ 1539177633Sdfr mtx_lock(&host->nh_lock); 1540177633Sdfr TAILQ_INSERT_TAIL(&host->nh_pending, af, af_link); 1541177633Sdfr mtx_unlock(&host->nh_lock); 1542177633Sdfr 1543177633Sdfr error = VOP_ADVLOCKASYNC(vs.vs_vp, NULL, F_SETLK, &fl, F_REMOTE, 1544177633Sdfr &af->af_task, &af->af_cookie); 1545177633Sdfr 1546177633Sdfr /* 1547177633Sdfr * If the lock completed synchronously, just free the 1548177633Sdfr * tracking structure now. 1549177633Sdfr */ 1550177633Sdfr if (error != EINPROGRESS) { 1551177633Sdfr mtx_lock(&host->nh_lock); 1552177633Sdfr TAILQ_REMOVE(&host->nh_pending, af, af_link); 1553177633Sdfr mtx_unlock(&host->nh_lock); 1554177633Sdfr xdr_free((xdrproc_t) xdr_nlm4_testargs, 1555177633Sdfr &af->af_granted); 1556177633Sdfr free(af, M_NLM); 1557177633Sdfr } else { 1558177633Sdfr if (nlm_debug_level >= 2) 1559177633Sdfr printf("NLM: pending async lock %p for %s " 1560177633Sdfr "(sysid %d)\n", 1561177633Sdfr af, host->nh_caller_name, sysid); 1562177633Sdfr /* 1563177633Sdfr * Don't vrele the vnode just yet - this must 1564177633Sdfr * wait until either the async callback 1565177633Sdfr * happens or the lock is cancelled. 1566177633Sdfr */ 1567177633Sdfr vs.vs_vp = NULL; 1568177633Sdfr } 1569177633Sdfr } else { 1570177633Sdfr error = VOP_ADVLOCK(vs.vs_vp, NULL, F_SETLK, &fl, F_REMOTE); 1571177633Sdfr } 1572177633Sdfr 1573177633Sdfr if (error) { 1574177633Sdfr if (error == EINPROGRESS) { 1575177633Sdfr result->stat.stat = nlm4_blocked; 1576177633Sdfr } else if (error == EDEADLK) { 1577177633Sdfr result->stat.stat = nlm4_deadlck; 1578177633Sdfr } else if (error == EAGAIN) { 1579177633Sdfr result->stat.stat = nlm4_denied; 1580177633Sdfr } else { 1581177633Sdfr result->stat.stat = nlm4_failed; 1582177633Sdfr } 1583177633Sdfr } else { 1584177633Sdfr if (monitor) 1585177633Sdfr nlm_host_monitor(host, argp->state); 1586177633Sdfr result->stat.stat = nlm4_granted; 1587177633Sdfr } 1588177633Sdfr 1589177633Sdfrout: 1590177633Sdfr nlm_release_vfs_state(&vs); 1591177633Sdfr 1592177633Sdfr return (host); 1593177633Sdfr} 1594177633Sdfr 1595177633Sdfrstruct nlm_host * 1596177633Sdfrnlm_do_cancel(nlm4_cancargs *argp, nlm4_res *result, struct svc_req *rqstp) 1597177633Sdfr{ 1598177633Sdfr fhandle_t fh; 1599177633Sdfr struct vfs_state vs; 1600177633Sdfr struct nlm_host *host; 1601177633Sdfr int error, sysid; 1602177633Sdfr struct flock fl; 1603177633Sdfr struct nlm_async_lock *af; 1604177633Sdfr 1605177633Sdfr memset(result, 0, sizeof(*result)); 1606177633Sdfr 1607177633Sdfr host = nlm_find_host_by_name(argp->alock.caller_name, rqstp); 1608177633Sdfr if (!host) { 1609177633Sdfr result->stat.stat = nlm4_denied_nolocks; 1610177633Sdfr return (NULL); 1611177633Sdfr } 1612177633Sdfr 1613177633Sdfr if (nlm_debug_level >= 3) 1614177633Sdfr printf("nlm_do_cancel(): caller_name = %s (sysid = %d)\n", 1615177633Sdfr host->nh_caller_name, host->nh_sysid); 1616177633Sdfr 1617177633Sdfr nlm_free_finished_locks(host); 1618177633Sdfr sysid = host->nh_sysid; 1619177633Sdfr 1620177633Sdfr nlm_convert_to_fhandle_t(&fh, &argp->alock.fh); 1621177633Sdfr nlm_copy_netobj(&result->cookie, &argp->cookie, M_RPC); 1622177633Sdfr 1623177633Sdfr if (time_uptime < nlm_grace_threshold) { 1624177633Sdfr result->stat.stat = nlm4_denied_grace_period; 1625177633Sdfr return (host); 1626177633Sdfr } 1627177633Sdfr 1628177633Sdfr error = nlm_get_vfs_state(host, rqstp, &fh, &vs); 1629177633Sdfr if (error) { 1630177633Sdfr result->stat.stat = nlm_convert_error(error); 1631177633Sdfr goto out; 1632177633Sdfr } 1633177633Sdfr 1634177633Sdfr fl.l_start = argp->alock.l_offset; 1635177633Sdfr fl.l_len = argp->alock.l_len; 1636177633Sdfr fl.l_pid = argp->alock.svid; 1637177633Sdfr fl.l_sysid = sysid; 1638177633Sdfr fl.l_whence = SEEK_SET; 1639177633Sdfr if (argp->exclusive) 1640177633Sdfr fl.l_type = F_WRLCK; 1641177633Sdfr else 1642177633Sdfr fl.l_type = F_RDLCK; 1643177633Sdfr 1644177633Sdfr /* 1645177633Sdfr * First we need to try and find the async lock request - if 1646177633Sdfr * there isn't one, we give up and return nlm4_denied. 1647177633Sdfr */ 1648177633Sdfr mtx_lock(&host->nh_lock); 1649177633Sdfr 1650177633Sdfr TAILQ_FOREACH(af, &host->nh_pending, af_link) { 1651177633Sdfr if (af->af_fl.l_start == fl.l_start 1652177633Sdfr && af->af_fl.l_len == fl.l_len 1653177633Sdfr && af->af_fl.l_pid == fl.l_pid 1654177633Sdfr && af->af_fl.l_type == fl.l_type) { 1655177633Sdfr break; 1656177633Sdfr } 1657177633Sdfr } 1658177633Sdfr 1659177633Sdfr if (!af) { 1660177633Sdfr mtx_unlock(&host->nh_lock); 1661177633Sdfr result->stat.stat = nlm4_denied; 1662177633Sdfr goto out; 1663177633Sdfr } 1664177633Sdfr 1665177633Sdfr error = nlm_cancel_async_lock(af); 1666177633Sdfr 1667177633Sdfr if (error) { 1668177633Sdfr result->stat.stat = nlm4_denied; 1669177633Sdfr } else { 1670177633Sdfr result->stat.stat = nlm4_granted; 1671177633Sdfr } 1672177633Sdfr 1673177633Sdfr mtx_unlock(&host->nh_lock); 1674177633Sdfr 1675177633Sdfrout: 1676177633Sdfr nlm_release_vfs_state(&vs); 1677177633Sdfr 1678177633Sdfr return (host); 1679177633Sdfr} 1680177633Sdfr 1681177633Sdfrstruct nlm_host * 1682177633Sdfrnlm_do_unlock(nlm4_unlockargs *argp, nlm4_res *result, struct svc_req *rqstp) 1683177633Sdfr{ 1684177633Sdfr fhandle_t fh; 1685177633Sdfr struct vfs_state vs; 1686177633Sdfr struct nlm_host *host; 1687177633Sdfr int error, sysid; 1688177633Sdfr struct flock fl; 1689177633Sdfr 1690177633Sdfr memset(result, 0, sizeof(*result)); 1691177633Sdfr 1692177633Sdfr host = nlm_find_host_by_name(argp->alock.caller_name, rqstp); 1693177633Sdfr if (!host) { 1694177633Sdfr result->stat.stat = nlm4_denied_nolocks; 1695177633Sdfr return (NULL); 1696177633Sdfr } 1697177633Sdfr 1698177633Sdfr if (nlm_debug_level >= 3) 1699177633Sdfr printf("nlm_do_unlock(): caller_name = %s (sysid = %d)\n", 1700177633Sdfr host->nh_caller_name, host->nh_sysid); 1701177633Sdfr 1702177633Sdfr nlm_free_finished_locks(host); 1703177633Sdfr sysid = host->nh_sysid; 1704177633Sdfr 1705177633Sdfr nlm_convert_to_fhandle_t(&fh, &argp->alock.fh); 1706177633Sdfr nlm_copy_netobj(&result->cookie, &argp->cookie, M_RPC); 1707177633Sdfr 1708177633Sdfr if (time_uptime < nlm_grace_threshold) { 1709177633Sdfr result->stat.stat = nlm4_denied_grace_period; 1710177633Sdfr return (host); 1711177633Sdfr } 1712177633Sdfr 1713177633Sdfr error = nlm_get_vfs_state(host, rqstp, &fh, &vs); 1714177633Sdfr if (error) { 1715177633Sdfr result->stat.stat = nlm_convert_error(error); 1716177633Sdfr goto out; 1717177633Sdfr } 1718177633Sdfr 1719177633Sdfr fl.l_start = argp->alock.l_offset; 1720177633Sdfr fl.l_len = argp->alock.l_len; 1721177633Sdfr fl.l_pid = argp->alock.svid; 1722177633Sdfr fl.l_sysid = sysid; 1723177633Sdfr fl.l_whence = SEEK_SET; 1724177633Sdfr fl.l_type = F_UNLCK; 1725177633Sdfr error = VOP_ADVLOCK(vs.vs_vp, NULL, F_UNLCK, &fl, F_REMOTE); 1726177633Sdfr 1727177633Sdfr /* 1728177633Sdfr * Ignore the error - there is no result code for failure, 1729177633Sdfr * only for grace period. 1730177633Sdfr */ 1731177633Sdfr result->stat.stat = nlm4_granted; 1732177633Sdfr 1733177633Sdfrout: 1734177633Sdfr nlm_release_vfs_state(&vs); 1735177633Sdfr 1736177633Sdfr return (host); 1737177633Sdfr} 1738177633Sdfr 1739177633Sdfrvoid 1740177633Sdfrnlm_do_free_all(nlm4_notify *argp) 1741177633Sdfr{ 1742177633Sdfr struct nlm_host *host, *thost; 1743177633Sdfr 1744177633Sdfr TAILQ_FOREACH_SAFE(host, &nlm_hosts, nh_link, thost) { 1745177633Sdfr if (!strcmp(host->nh_caller_name, argp->name)) 1746177633Sdfr nlm_host_notify(host, argp->state, FALSE); 1747177633Sdfr } 1748177633Sdfr} 1749177633Sdfr 1750177633Sdfr#define _PATH_RPCLOCKDSOCK "/var/run/rpclockd.sock" 1751177633Sdfr 1752177633Sdfr/* 1753177633Sdfr * Make a connection to the userland lockd - we push anything we can't 1754177633Sdfr * handle out to userland. 1755177633Sdfr */ 1756177633SdfrCLIENT * 1757177633Sdfrnlm_user_lockd(void) 1758177633Sdfr{ 1759177633Sdfr struct sockaddr_un sun; 1760177633Sdfr struct netconfig *nconf; 1761177633Sdfr struct timeval zero; 1762177633Sdfr 1763177633Sdfr if (nlm_lockd) 1764177633Sdfr return (nlm_lockd); 1765177633Sdfr 1766177633Sdfr sun.sun_family = AF_LOCAL; 1767177633Sdfr strcpy(sun.sun_path, _PATH_RPCLOCKDSOCK); 1768177633Sdfr sun.sun_len = SUN_LEN(&sun); 1769177633Sdfr 1770177633Sdfr nconf = getnetconfigent("local"); 1771177633Sdfr nlm_lockd = clnt_reconnect_create(nconf, (struct sockaddr *) &sun, 1772177633Sdfr NLM_PROG, NLM_VERS4, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 1773177633Sdfr 1774177633Sdfr /* 1775177633Sdfr * Set the send timeout to zero - we only use this rpc handle 1776177633Sdfr * for sending async replies which have no return value. 1777177633Sdfr */ 1778177633Sdfr zero.tv_sec = 0; 1779177633Sdfr zero.tv_usec = 0; 1780177633Sdfr CLNT_CONTROL(nlm_lockd, CLSET_TIMEOUT, &zero); 1781177633Sdfr 1782177633Sdfr return (nlm_lockd); 1783177633Sdfr} 1784