1 /* $NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $ */
6 * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed for the FreeBSD project
19 * 4. Neither the name of the author nor the names of any co-contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #include <sys/cdefs.h>
39 __RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $");
43 * main() function for NFS lock daemon. Most of the code in this
44 * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x.
46 * The actual program logic is in the file lock_proc.c
49 #include <sys/param.h>
50 #include <sys/linker.h>
51 #include <sys/module.h>
52 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
67 #include <netconfig.h>
71 #include <rpc/rpc_com.h>
72 #include <rpcsvc/sm_inter.h>
75 #include <rpcsvc/nlm_prot.h>
77 int debug_level
= 0; /* 0 = no debugging syslog() calls */
83 int kernel_lockd_client
;
86 char **hosts
, *svcport_str
= NULL
;
89 char **addrs
; /* actually (netid, uaddr) pairs */
90 int naddrs
; /* count of how many (netid, uaddr) pairs */
92 void create_service(struct netconfig
*nconf
);
93 void lookup_addresses(struct netconfig
*nconf
);
95 void nlm_prog_0(struct svc_req
*, SVCXPRT
*);
96 void nlm_prog_1(struct svc_req
*, SVCXPRT
*);
97 void nlm_prog_3(struct svc_req
*, SVCXPRT
*);
98 void nlm_prog_4(struct svc_req
*, SVCXPRT
*);
99 void out_of_mem(void);
102 void sigalarm_handler(void);
105 * XXX move to some header file.
107 #define _PATH_RPCLOCKDSOCK "/var/run/rpclockd.sock"
110 main(int argc
, char **argv
)
114 char *endptr
, **hosts_bak
;
115 struct sigaction sigalarm
;
116 int grace_period
= 30;
117 struct netconfig
*nconf
;
119 int maxrec
= RPC_MAXDATASIZE
;
120 in_port_t svcport
= 0;
122 while ((ch
= getopt(argc
, argv
, "d:g:h:p:")) != (-1)) {
125 debug_level
= atoi(optarg
);
132 grace_period
= atoi(optarg
);
141 hosts_bak
= realloc(hosts
, nhosts
* sizeof(char *));
142 if (hosts_bak
== NULL
) {
144 for (i
= 0; i
< nhosts
; i
++)
151 hosts
[nhosts
- 1] = strdup(optarg
);
152 if (hosts
[nhosts
- 1] == NULL
) {
153 for (i
= 0; i
< (nhosts
- 1); i
++)
161 svcport
= (in_port_t
)strtoul(optarg
, &endptr
, 10);
162 if (endptr
== NULL
|| *endptr
!= '\0' ||
163 svcport
== 0 || svcport
>= IPPORT_MAX
)
165 svcport_str
= strdup(optarg
);
173 if (geteuid()) { /* This command allowed only to root */
174 fprintf(stderr
, "Sorry. You are not superuser\n");
178 kernel_lockd
= FALSE
;
179 kernel_lockd_client
= FALSE
;
180 if (modfind("nfslockd") < 0) {
181 if (kldload("nfslockd") < 0) {
182 fprintf(stderr
, "Can't find or load kernel support for rpc.lockd - using non-kernel implementation\n");
190 if (getosreldate() >= 800040)
191 kernel_lockd_client
= TRUE
;
194 (void)rpcb_unset(NLM_PROG
, NLM_SM
, NULL
);
195 (void)rpcb_unset(NLM_PROG
, NLM_VERS
, NULL
);
196 (void)rpcb_unset(NLM_PROG
, NLM_VERSX
, NULL
);
197 (void)rpcb_unset(NLM_PROG
, NLM_VERS4
, NULL
);
200 * Check if IPv6 support is present.
202 s
= socket(AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
);
208 rpc_control(RPC_SVC_CONNMAXREC_SET
, &maxrec
);
211 * If no hosts were specified, add a wildcard entry to bind to
212 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
216 hosts
= malloc(sizeof(char**));
225 hosts_bak
= realloc(hosts
, (nhosts
+ 2) *
227 if (hosts_bak
== NULL
) {
228 for (i
= 0; i
< nhosts
; i
++)
236 hosts
[nhosts
- 2] = "::1";
238 hosts_bak
= realloc(hosts
, (nhosts
+ 1) * sizeof(char *));
239 if (hosts_bak
== NULL
) {
240 for (i
= 0; i
< nhosts
; i
++)
250 hosts
[nhosts
- 1] = "127.0.0.1";
254 if (!kernel_lockd_client
) {
256 * For the case where we have a kernel lockd but it
257 * doesn't provide client locking, we run a cut-down
258 * RPC service on a local-domain socket. The kernel's
259 * RPC server will pass what it can't handle (mainly
260 * client replies) down to us.
262 struct sockaddr_un sun
;
266 memset(&sun
, 0, sizeof sun
);
267 sun
.sun_family
= AF_LOCAL
;
268 unlink(_PATH_RPCLOCKDSOCK
);
269 strcpy(sun
.sun_path
, _PATH_RPCLOCKDSOCK
);
270 sun
.sun_len
= SUN_LEN(&sun
);
271 fd
= socket(AF_LOCAL
, SOCK_STREAM
, 0);
273 err(1, "Can't create local lockd socket");
275 oldmask
= umask(S_IXUSR
|S_IRWXG
|S_IRWXO
);
276 if (bind(fd
, (struct sockaddr
*) &sun
, sun
.sun_len
) < 0) {
277 err(1, "Can't bind local lockd socket");
280 if (listen(fd
, SOMAXCONN
) < 0) {
281 err(1, "Can't listen on local lockd socket");
283 xprt
= svc_vc_create(fd
, RPC_MAXDATASIZE
, RPC_MAXDATASIZE
);
285 err(1, "Can't create transport for local lockd socket");
287 if (!svc_reg(xprt
, NLM_PROG
, NLM_VERS4
, nlm_prog_4
, NULL
)) {
288 err(1, "Can't register service for local lockd socket");
293 * We need to look up the addresses so that we can
294 * hand uaddrs (ascii encoded address+port strings) to
297 nc_handle
= setnetconfig();
298 while ((nconf
= getnetconfig(nc_handle
))) {
299 /* We want to listen only on udp6, tcp6, udp, tcp transports */
300 if (nconf
->nc_flag
& NC_VISIBLE
) {
301 /* Skip if there's no IPv6 support */
302 if (have_v6
== 0 && strcmp(nconf
->nc_protofmly
, "inet6") == 0) {
305 lookup_addresses(nconf
);
309 endnetconfig(nc_handle
);
311 nc_handle
= setnetconfig();
312 while ((nconf
= getnetconfig(nc_handle
))) {
313 /* We want to listen only on udp6, tcp6, udp, tcp transports */
314 if (nconf
->nc_flag
& NC_VISIBLE
) {
315 /* Skip if there's no IPv6 support */
316 if (have_v6
== 0 && strcmp(nconf
->nc_protofmly
, "inet6") == 0) {
319 create_service(nconf
);
323 endnetconfig(nc_handle
);
327 * Note that it is NOT sensible to run this program from inetd - the
328 * protocol assumes that it will run immediately at boot time.
330 if (daemon(0, debug_level
> 0)) {
331 err(1, "cannot fork");
335 openlog("rpc.lockd", 0, LOG_DAEMON
);
337 syslog(LOG_INFO
, "Starting, debug level %d", debug_level
);
339 syslog(LOG_INFO
, "Starting");
341 sigalarm
.sa_handler
= (sig_t
) sigalarm_handler
;
342 sigemptyset(&sigalarm
.sa_mask
);
343 sigalarm
.sa_flags
= SA_RESETHAND
; /* should only happen once */
344 sigalarm
.sa_flags
|= SA_RESTART
;
345 if (sigaction(SIGALRM
, &sigalarm
, NULL
) != 0) {
346 syslog(LOG_WARNING
, "sigaction(SIGALRM) failed: %s",
352 if (!kernel_lockd_client
) {
354 client_pid
= client_request();
357 * Create a child process to enter the kernel and then
358 * wait for RPCs on our local domain socket.
361 nlm_syscall(debug_level
, grace_period
,
367 * The kernel lockd implementation provides
368 * both client and server so we don't need to
371 nlm_syscall(debug_level
, grace_period
, naddrs
, addrs
);
379 client_pid
= client_request();
381 svc_run(); /* Should never return */
387 * This routine creates and binds sockets on the appropriate
388 * addresses. It gets called one time for each transport and
389 * registrates the service with rpcbind on that trasport.
392 create_service(struct netconfig
*nconf
)
394 struct addrinfo hints
, *res
= NULL
;
395 struct sockaddr_in
*sin
;
396 struct sockaddr_in6
*sin6
;
397 struct __rpc_sockinfo si
;
398 struct netbuf servaddr
;
399 SVCXPRT
*transp
= NULL
;
405 u_int32_t host_addr
[4]; /* IPv4 or IPv6 */
407 if ((nconf
->nc_semantics
!= NC_TPI_CLTS
) &&
408 (nconf
->nc_semantics
!= NC_TPI_COTS
) &&
409 (nconf
->nc_semantics
!= NC_TPI_COTS_ORD
))
410 return; /* not my type */
413 * XXX - using RPC library internal functions.
415 if (!__rpc_nconf2sockinfo(nconf
, &si
)) {
416 syslog(LOG_ERR
, "cannot get information for %s",
421 /* Get rpc.statd's address on this transport */
422 memset(&hints
, 0, sizeof hints
);
423 hints
.ai_flags
= AI_PASSIVE
;
424 hints
.ai_family
= si
.si_af
;
425 hints
.ai_socktype
= si
.si_socktype
;
426 hints
.ai_protocol
= si
.si_proto
;
429 * Bind to specific IPs if asked to
432 while (nhostsbak
> 0) {
436 * XXX - using RPC library internal functions.
438 if ((fd
= __rpc_nconf2fd(nconf
)) < 0) {
439 syslog(LOG_ERR
, "cannot create socket for %s",
444 switch (hints
.ai_family
) {
446 if (inet_pton(AF_INET
, hosts
[nhostsbak
],
448 hints
.ai_flags
&= AI_NUMERICHOST
;
451 * Skip if we have an AF_INET6 address.
453 if (inet_pton(AF_INET6
, hosts
[nhostsbak
],
461 if (inet_pton(AF_INET6
, hosts
[nhostsbak
],
463 hints
.ai_flags
&= AI_NUMERICHOST
;
466 * Skip if we have an AF_INET address.
468 if (inet_pton(AF_INET
, hosts
[nhostsbak
],
480 * If no hosts were specified, just bind to INADDR_ANY
482 if (strcmp("*", hosts
[nhostsbak
]) == 0) {
483 if (svcport_str
== NULL
) {
484 res
= malloc(sizeof(struct addrinfo
));
487 res
->ai_flags
= hints
.ai_flags
;
488 res
->ai_family
= hints
.ai_family
;
489 res
->ai_protocol
= hints
.ai_protocol
;
490 switch (res
->ai_family
) {
492 sin
= malloc(sizeof(struct sockaddr_in
));
495 sin
->sin_family
= AF_INET
;
496 sin
->sin_port
= htons(0);
497 sin
->sin_addr
.s_addr
= htonl(INADDR_ANY
);
498 res
->ai_addr
= (struct sockaddr
*) sin
;
499 res
->ai_addrlen
= (socklen_t
)
500 sizeof(res
->ai_addr
);
503 sin6
= malloc(sizeof(struct sockaddr_in6
));
506 sin6
->sin6_family
= AF_INET6
;
507 sin6
->sin6_port
= htons(0);
508 sin6
->sin6_addr
= in6addr_any
;
509 res
->ai_addr
= (struct sockaddr
*) sin6
;
510 res
->ai_addrlen
= (socklen_t
) sizeof(res
->ai_addr
);
516 if ((aicode
= getaddrinfo(NULL
, svcport_str
,
517 &hints
, &res
)) != 0) {
519 "cannot get local address for %s: %s",
521 gai_strerror(aicode
));
526 if ((aicode
= getaddrinfo(hosts
[nhostsbak
], svcport_str
,
527 &hints
, &res
)) != 0) {
529 "cannot get local address for %s: %s",
530 nconf
->nc_netid
, gai_strerror(aicode
));
535 r
= bindresvport_sa(fd
, res
->ai_addr
);
537 syslog(LOG_ERR
, "bindresvport_sa: %m");
541 if (nconf
->nc_semantics
!= NC_TPI_CLTS
)
542 listen(fd
, SOMAXCONN
);
544 transp
= svc_tli_create(fd
, nconf
, NULL
,
545 RPC_MAXDATASIZE
, RPC_MAXDATASIZE
);
547 if (transp
!= (SVCXPRT
*) NULL
) {
548 if (!svc_reg(transp
, NLM_PROG
, NLM_SM
, nlm_prog_0
,
551 "can't register %s NLM_PROG, NLM_SM service",
554 if (!svc_reg(transp
, NLM_PROG
, NLM_VERS
, nlm_prog_1
,
557 "can't register %s NLM_PROG, NLM_VERS service",
560 if (!svc_reg(transp
, NLM_PROG
, NLM_VERSX
, nlm_prog_3
,
563 "can't register %s NLM_PROG, NLM_VERSX service",
566 if (!svc_reg(transp
, NLM_PROG
, NLM_VERS4
, nlm_prog_4
,
569 "can't register %s NLM_PROG, NLM_VERS4 service",
573 syslog(LOG_WARNING
, "can't create %s services",
576 if (registered
== 0) {
578 memset(&hints
, 0, sizeof hints
);
579 hints
.ai_flags
= AI_PASSIVE
;
580 hints
.ai_family
= si
.si_af
;
581 hints
.ai_socktype
= si
.si_socktype
;
582 hints
.ai_protocol
= si
.si_proto
;
584 if (svcport_str
== NULL
) {
585 svcport_str
= malloc(NI_MAXSERV
* sizeof(char));
586 if (svcport_str
== NULL
)
589 if (getnameinfo(res
->ai_addr
,
590 res
->ai_addr
->sa_len
, NULL
, NI_MAXHOST
,
591 svcport_str
, NI_MAXSERV
* sizeof(char),
592 NI_NUMERICHOST
| NI_NUMERICSERV
))
593 errx(1, "Cannot get port number");
596 if((aicode
= getaddrinfo(NULL
, svcport_str
, &hints
,
598 syslog(LOG_ERR
, "cannot get local address: %s",
599 gai_strerror(aicode
));
603 servaddr
.buf
= malloc(res
->ai_addrlen
);
604 memcpy(servaddr
.buf
, res
->ai_addr
, res
->ai_addrlen
);
605 servaddr
.len
= res
->ai_addrlen
;
607 rpcb_set(NLM_PROG
, NLM_SM
, nconf
, &servaddr
);
608 rpcb_set(NLM_PROG
, NLM_VERS
, nconf
, &servaddr
);
609 rpcb_set(NLM_PROG
, NLM_VERSX
, nconf
, &servaddr
);
610 rpcb_set(NLM_PROG
, NLM_VERS4
, nconf
, &servaddr
);
619 * Look up addresses for the kernel to create transports for.
622 lookup_addresses(struct netconfig
*nconf
)
624 struct addrinfo hints
, *res
= NULL
;
625 struct sockaddr_in
*sin
;
626 struct sockaddr_in6
*sin6
;
627 struct __rpc_sockinfo si
;
628 struct netbuf servaddr
;
629 SVCXPRT
*transp
= NULL
;
634 u_int32_t host_addr
[4]; /* IPv4 or IPv6 */
637 if ((nconf
->nc_semantics
!= NC_TPI_CLTS
) &&
638 (nconf
->nc_semantics
!= NC_TPI_COTS
) &&
639 (nconf
->nc_semantics
!= NC_TPI_COTS_ORD
))
640 return; /* not my type */
643 * XXX - using RPC library internal functions.
645 if (!__rpc_nconf2sockinfo(nconf
, &si
)) {
646 syslog(LOG_ERR
, "cannot get information for %s",
651 /* Get rpc.statd's address on this transport */
652 memset(&hints
, 0, sizeof hints
);
653 hints
.ai_flags
= AI_PASSIVE
;
654 hints
.ai_family
= si
.si_af
;
655 hints
.ai_socktype
= si
.si_socktype
;
656 hints
.ai_protocol
= si
.si_proto
;
659 * Bind to specific IPs if asked to
662 while (nhostsbak
> 0) {
665 switch (hints
.ai_family
) {
667 if (inet_pton(AF_INET
, hosts
[nhostsbak
],
669 hints
.ai_flags
&= AI_NUMERICHOST
;
672 * Skip if we have an AF_INET6 address.
674 if (inet_pton(AF_INET6
, hosts
[nhostsbak
],
681 if (inet_pton(AF_INET6
, hosts
[nhostsbak
],
683 hints
.ai_flags
&= AI_NUMERICHOST
;
686 * Skip if we have an AF_INET address.
688 if (inet_pton(AF_INET
, hosts
[nhostsbak
],
699 * If no hosts were specified, just bind to INADDR_ANY
701 if (strcmp("*", hosts
[nhostsbak
]) == 0) {
702 if (svcport_str
== NULL
) {
703 res
= malloc(sizeof(struct addrinfo
));
706 res
->ai_flags
= hints
.ai_flags
;
707 res
->ai_family
= hints
.ai_family
;
708 res
->ai_protocol
= hints
.ai_protocol
;
709 switch (res
->ai_family
) {
711 sin
= malloc(sizeof(struct sockaddr_in
));
714 sin
->sin_family
= AF_INET
;
715 sin
->sin_port
= htons(0);
716 sin
->sin_addr
.s_addr
= htonl(INADDR_ANY
);
717 res
->ai_addr
= (struct sockaddr
*) sin
;
718 res
->ai_addrlen
= (socklen_t
)
719 sizeof(res
->ai_addr
);
722 sin6
= malloc(sizeof(struct sockaddr_in6
));
725 sin6
->sin6_family
= AF_INET6
;
726 sin6
->sin6_port
= htons(0);
727 sin6
->sin6_addr
= in6addr_any
;
728 res
->ai_addr
= (struct sockaddr
*) sin6
;
729 res
->ai_addrlen
= (socklen_t
) sizeof(res
->ai_addr
);
735 if ((aicode
= getaddrinfo(NULL
, svcport_str
,
736 &hints
, &res
)) != 0) {
738 "cannot get local address for %s: %s",
740 gai_strerror(aicode
));
745 if ((aicode
= getaddrinfo(hosts
[nhostsbak
], svcport_str
,
746 &hints
, &res
)) != 0) {
748 "cannot get local address for %s: %s",
749 nconf
->nc_netid
, gai_strerror(aicode
));
754 servaddr
.len
= servaddr
.maxlen
= res
->ai_addr
->sa_len
;
755 servaddr
.buf
= res
->ai_addr
;
756 uaddr
= taddr2uaddr(nconf
, &servaddr
);
758 addrs
= realloc(addrs
, 2 * (naddrs
+ 1) * sizeof(char *));
761 addrs
[2 * naddrs
] = strdup(nconf
->nc_netid
);
762 addrs
[2 * naddrs
+ 1] = uaddr
;
768 sigalarm_handler(void)
777 errx(1, "usage: rpc.lockd [-d <debuglevel>]"
778 " [-g <grace period>] [-h <bindip>] [-p <port>]");
783 * Reset the NSM state-of-the-world and acquire its state.
791 char name
[] = "NFS NLM";
792 char localhost
[] = "localhost";
796 * The my_id structure isn't used by the SM_UNMON_ALL call, as far
797 * as I know. Leave it empty for now.
799 memset(&id
, 0, sizeof(id
));
804 * The statd program must already be registered when lockd runs.
807 ret
= callrpc("localhost", SM_PROG
, SM_VERS
, SM_UNMON_ALL
,
808 (xdrproc_t
)xdr_my_id
, &id
, (xdrproc_t
)xdr_sm_stat
, &stat
);
809 if (ret
== RPC_PROGUNAVAIL
) {
810 syslog(LOG_WARNING
, "%lu %s", SM_PROG
,
819 syslog(LOG_ERR
, "%lu %s", SM_PROG
, clnt_sperrno(ret
));
823 nsm_state
= stat
.state
;
825 /* setup constant data for SM_MON calls */
826 mon_host
.mon_id
.my_id
.my_name
= localhost
;
827 mon_host
.mon_id
.my_id
.my_prog
= NLM_PROG
;
828 mon_host
.mon_id
.my_id
.my_vers
= NLM_SM
;
829 mon_host
.mon_id
.my_id
.my_proc
= NLM_SM_NOTIFY
; /* bsdi addition */
833 * Out of memory, fatal
837 syslog(LOG_ERR
, "out of memory");