xref: /freebsd/usr.sbin/rpc.lockd/lockd.c (revision a0409676120c1e558d0ade943019934e0f15118d)
1 /*	$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $	*/
2 /*	$FreeBSD$ */
3 
4 /*-
5  * SPDX-License-Identifier: BSD-4-Clause
6  *
7  * Copyright (c) 1995
8  *	A.R. Gordon (andrew.gordon@net-tel.co.uk).  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed for the FreeBSD project
21  * 4. Neither the name of the author nor the names of any co-contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $");
42 #endif
43 
44 /*
45  * main() function for NFS lock daemon.  Most of the code in this
46  * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x.
47  *
48  * The actual program logic is in the file lock_proc.c
49  */
50 
51 #include <sys/param.h>
52 #include <sys/linker.h>
53 #include <sys/module.h>
54 #include <sys/socket.h>
55 #include <sys/stat.h>
56 
57 #include <netinet/in.h>
58 #include <arpa/inet.h>
59 
60 #include <err.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <errno.h>
64 #include <syslog.h>
65 #include <signal.h>
66 #include <string.h>
67 #include <unistd.h>
68 #include <libutil.h>
69 #include <netconfig.h>
70 #include <netdb.h>
71 
72 #include <rpc/rpc.h>
73 #include <rpc/rpc_com.h>
74 #include <rpcsvc/sm_inter.h>
75 
76 #include "lockd.h"
77 #include <rpcsvc/nlm_prot.h>
78 
79 #define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
80 
81 int		debug_level = 0;	/* 0 = no debugging syslog() calls */
82 int		_rpcsvcdirty = 0;
83 
84 int grace_expired;
85 int nsm_state;
86 int kernel_lockd;
87 int kernel_lockd_client;
88 pid_t client_pid;
89 struct mon mon_host;
90 char **hosts, *svcport_str = NULL;
91 static int	mallocd_svcport = 0;
92 static int	*sock_fd;
93 static int	sock_fdcnt;
94 static int	sock_fdpos;
95 int nhosts = 0;
96 int xcreated = 0;
97 char **addrs;			/* actually (netid, uaddr) pairs */
98 int naddrs;			/* count of how many (netid, uaddr) pairs */
99 char localhost[] = "localhost";
100 
101 static int	create_service(struct netconfig *nconf);
102 static void	complete_service(struct netconfig *nconf, char *port_str);
103 static void	clearout_service(void);
104 static void	out_of_mem(void) __dead2;
105 void	init_nsm(void);
106 void	usage(void);
107 
108 void sigalarm_handler(void);
109 
110 /*
111  * XXX move to some header file.
112  */
113 #define _PATH_RPCLOCKDSOCK	"/var/run/rpclockd.sock"
114 
115 int
116 main(int argc, char **argv)
117 {
118 	int ch, i, s;
119 	void *nc_handle;
120 	char *endptr, **hosts_bak;
121 	struct sigaction sigalarm;
122 	int grace_period = 30;
123 	int foreground = 0;
124 	struct netconfig *nconf;
125 	int have_v6 = 1;
126 	int maxrec = RPC_MAXDATASIZE;
127 	in_port_t svcport = 0;
128 	int attempt_cnt, port_len, port_pos, ret;
129 	char **port_list;
130 
131 	while ((ch = getopt(argc, argv, "d:Fg:h:p:")) != (-1)) {
132 		switch (ch) {
133 		case 'd':
134 			debug_level = atoi(optarg);
135 			if (!debug_level) {
136 				usage();
137 				/* NOTREACHED */
138 			}
139 			break;
140 		case 'F':
141 			foreground = 1;
142 			break;
143 		case 'g':
144 			grace_period = atoi(optarg);
145 			if (!grace_period) {
146 				usage();
147 				/* NOTREACHED */
148 			}
149 			break;
150 		case 'h':
151 			++nhosts;
152 			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
153 			if (hosts_bak == NULL) {
154 				if (hosts != NULL) {
155 					for (i = 0; i < nhosts; i++)
156 						free(hosts[i]);
157 					free(hosts);
158 					out_of_mem();
159 				}
160 			}
161 			hosts = hosts_bak;
162 			hosts[nhosts - 1] = strdup(optarg);
163 			if (hosts[nhosts - 1] == NULL) {
164 				for (i = 0; i < (nhosts - 1); i++)
165 					free(hosts[i]);
166 				free(hosts);
167 				out_of_mem();
168 			}
169 			break;
170 		case 'p':
171 			endptr = NULL;
172 			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
173 			if (endptr == NULL || *endptr != '\0' ||
174 			    svcport == 0 || svcport >= IPPORT_MAX)
175 				usage();
176 			svcport_str = strdup(optarg);
177 			break;
178 		default:
179 			usage();
180 			/* NOTREACHED */
181 		}
182 	}
183 	if (geteuid()) { /* This command allowed only to root */
184 		fprintf(stderr, "Sorry. You are not superuser\n");
185 		exit(1);
186         }
187 
188 	kernel_lockd = FALSE;
189 	kernel_lockd_client = FALSE;
190 	if (modfind("nfslockd") < 0) {
191 		if (kldload("nfslockd") < 0) {
192 			fprintf(stderr, "Can't find or load kernel support for rpc.lockd - using non-kernel implementation\n");
193 		} else {
194 			kernel_lockd = TRUE;
195 		}
196 	} else {
197 		kernel_lockd = TRUE;
198 	}
199 	if (kernel_lockd) {
200 		if (getosreldate() >= 800040)
201 			kernel_lockd_client = TRUE;
202 	}
203 
204 	(void)rpcb_unset(NLM_PROG, NLM_SM, NULL);
205 	(void)rpcb_unset(NLM_PROG, NLM_VERS, NULL);
206 	(void)rpcb_unset(NLM_PROG, NLM_VERSX, NULL);
207 	(void)rpcb_unset(NLM_PROG, NLM_VERS4, NULL);
208 
209 	/*
210 	 * Check if IPv6 support is present.
211 	 */
212 	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
213 	if (s < 0)
214 		have_v6 = 0;
215 	else
216 		close(s);
217 
218 	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
219 
220 	/*
221 	 * If no hosts were specified, add a wildcard entry to bind to
222 	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
223 	 * list.
224 	 */
225 	if (nhosts == 0) {
226 		hosts = malloc(sizeof(char *));
227 		if (hosts == NULL)
228 			out_of_mem();
229 
230 		hosts[0] = strdup("*");
231 		nhosts = 1;
232 	} else {
233 		if (have_v6) {
234 			hosts_bak = realloc(hosts, (nhosts + 2) *
235 			    sizeof(char *));
236 			if (hosts_bak == NULL) {
237 				for (i = 0; i < nhosts; i++)
238 					free(hosts[i]);
239 				free(hosts);
240 				out_of_mem();
241 			} else
242 				hosts = hosts_bak;
243 
244 			nhosts += 2;
245 			hosts[nhosts - 2] = strdup("::1");
246 		} else {
247 			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
248 			if (hosts_bak == NULL) {
249 				for (i = 0; i < nhosts; i++)
250 					free(hosts[i]);
251 
252 				free(hosts);
253 				out_of_mem();
254 			} else {
255 				nhosts += 1;
256 				hosts = hosts_bak;
257 			}
258 		}
259 		hosts[nhosts - 1] = strdup("127.0.0.1");
260 	}
261 
262 	if (kernel_lockd) {
263 		if (!kernel_lockd_client) {
264 			/*
265 			 * For the case where we have a kernel lockd but it
266 			 * doesn't provide client locking, we run a cut-down
267 			 * RPC service on a local-domain socket. The kernel's
268 			 * RPC server will pass what it can't handle (mainly
269 			 * client replies) down to us.
270 			 */
271 			struct sockaddr_un sun;
272 			int fd, oldmask;
273 			SVCXPRT *xprt;
274 
275 			memset(&sun, 0, sizeof sun);
276 			sun.sun_family = AF_LOCAL;
277 			unlink(_PATH_RPCLOCKDSOCK);
278 			strcpy(sun.sun_path, _PATH_RPCLOCKDSOCK);
279 			sun.sun_len = SUN_LEN(&sun);
280 			fd = socket(AF_LOCAL, SOCK_STREAM, 0);
281 			if (!fd) {
282 				err(1, "Can't create local lockd socket");
283 			}
284 			oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
285 			if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) {
286 				err(1, "Can't bind local lockd socket");
287 			}
288 			umask(oldmask);
289 			if (listen(fd, SOMAXCONN) < 0) {
290 				err(1, "Can't listen on local lockd socket");
291 			}
292 			xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
293 			if (!xprt) {
294 				err(1, "Can't create transport for local lockd socket");
295 			}
296 			if (!svc_reg(xprt, NLM_PROG, NLM_VERS4, nlm_prog_4, NULL)) {
297 				err(1, "Can't register service for local lockd socket");
298 			}
299 		}
300 
301 		/*
302 		 * We need to look up the addresses so that we can
303 		 * hand uaddrs (ascii encoded address+port strings) to
304 		 * the kernel.
305 		 */
306 		nc_handle = setnetconfig();
307 		while ((nconf = getnetconfig(nc_handle))) {
308 			/* We want to listen only on udp6, tcp6, udp, tcp transports */
309 			if (nconf->nc_flag & NC_VISIBLE) {
310 				/* Skip if there's no IPv6 support */
311 				if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
312 					/* DO NOTHING */
313 				} else {
314 					create_service(nconf);
315 				}
316 			}
317 		}
318 		endnetconfig(nc_handle);
319 	} else {
320 		attempt_cnt = 1;
321 		sock_fdcnt = 0;
322 		sock_fd = NULL;
323 		port_list = NULL;
324 		port_len = 0;
325 		nc_handle = setnetconfig();
326 		while ((nconf = getnetconfig(nc_handle))) {
327 			/* We want to listen only on udp6, tcp6, udp, tcp transports */
328 			if (nconf->nc_flag & NC_VISIBLE) {
329 				/* Skip if there's no IPv6 support */
330 				if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
331 					/* DO NOTHING */
332 				} else {
333 					ret = create_service(nconf);
334 					if (ret == 1)
335 						/* Ignore this call */
336 						continue;
337 					if (ret < 0) {
338 						/*
339 						 * Failed to bind port, so close
340 						 * off all sockets created and
341 						 * try again if the port# was
342 						 * dynamically assigned via
343 						 * bind(2).
344 						 */
345 						clearout_service();
346 						if (mallocd_svcport != 0 &&
347 						    attempt_cnt <
348 						    GETPORT_MAXTRY) {
349 							free(svcport_str);
350 							svcport_str = NULL;
351 							mallocd_svcport = 0;
352 						} else {
353 							errno = EADDRINUSE;
354 							syslog(LOG_ERR,
355 							 "bindresvport_sa: %m");
356 							exit(1);
357 						}
358 
359 						/*
360 						 * Start over at the first
361 						 * service.
362 						 */
363 						free(sock_fd);
364 						sock_fdcnt = 0;
365 						sock_fd = NULL;
366 						nc_handle = setnetconfig();
367 						attempt_cnt++;
368 					} else if (mallocd_svcport != 0 &&
369 					    attempt_cnt == GETPORT_MAXTRY) {
370 						/*
371 						 * For the last attempt, allow
372 						 * different port #s for each
373 						 * nconf by saving the
374 						 * svcport_str and setting it
375 						 * back to NULL.
376 						 */
377 						port_list = realloc(port_list,
378 						    (port_len + 1) *
379 						    sizeof(char *));
380 						if (port_list == NULL)
381 							out_of_mem();
382 						port_list[port_len++] =
383 						    svcport_str;
384 						svcport_str = NULL;
385 						mallocd_svcport = 0;
386 					}
387 				}
388 			}
389 		}
390 
391 		/*
392 		 * Successfully bound the ports, so call complete_service() to
393 		 * do the rest of the setup on the service(s).
394 		 */
395 		sock_fdpos = 0;
396 		port_pos = 0;
397 		nc_handle = setnetconfig();
398 		while ((nconf = getnetconfig(nc_handle))) {
399 			/* We want to listen only on udp6, tcp6, udp, tcp transports */
400 			if (nconf->nc_flag & NC_VISIBLE) {
401 				/* Skip if there's no IPv6 support */
402 				if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
403 					/* DO NOTHING */
404 				} else if (port_list != NULL) {
405 					if (port_pos >= port_len) {
406 						syslog(LOG_ERR,
407 						    "too many port#s");
408 						exit(1);
409 					}
410 					complete_service(nconf,
411 					    port_list[port_pos++]);
412 				} else
413 					complete_service(nconf, svcport_str);
414 			}
415 		}
416 		endnetconfig(nc_handle);
417 		free(sock_fd);
418 		if (port_list != NULL) {
419 			for (port_pos = 0; port_pos < port_len; port_pos++)
420 				free(port_list[port_pos]);
421 			free(port_list);
422 		}
423 	}
424 
425 	/*
426 	 * Note that it is NOT sensible to run this program from inetd - the
427 	 * protocol assumes that it will run immediately at boot time.
428 	 */
429 	if ((foreground == 0) && daemon(0, debug_level > 0)) {
430 		err(1, "cannot fork");
431 		/* NOTREACHED */
432 	}
433 
434 	openlog("rpc.lockd", 0, LOG_DAEMON);
435 	if (debug_level)
436 		syslog(LOG_INFO, "Starting, debug level %d", debug_level);
437 	else
438 		syslog(LOG_INFO, "Starting");
439 
440 	sigalarm.sa_handler = (sig_t) sigalarm_handler;
441 	sigemptyset(&sigalarm.sa_mask);
442 	sigalarm.sa_flags = SA_RESETHAND; /* should only happen once */
443 	sigalarm.sa_flags |= SA_RESTART;
444 	if (sigaction(SIGALRM, &sigalarm, NULL) != 0) {
445 		syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s",
446 		    strerror(errno));
447 		exit(1);
448 	}
449 
450 	if (kernel_lockd) {
451 		if (!kernel_lockd_client) {
452 			init_nsm();
453 			client_pid = client_request();
454 
455 			/*
456 			 * Create a child process to enter the kernel and then
457 			 * wait for RPCs on our local domain socket.
458 			 */
459 			if (!fork())
460 				nlm_syscall(debug_level, grace_period,
461 				    naddrs, addrs);
462 			else
463 				svc_run();
464 		} else {
465 			/*
466 			 * The kernel lockd implementation provides
467 			 * both client and server so we don't need to
468 			 * do anything else.
469 			 */
470 			nlm_syscall(debug_level, grace_period, naddrs, addrs);
471 		}
472 	} else {
473 		grace_expired = 0;
474 		alarm(grace_period);
475 
476 		init_nsm();
477 
478 		client_pid = client_request();
479 
480 		svc_run();		/* Should never return */
481 	}
482 	exit(1);
483 }
484 
485 /*
486  * This routine creates and binds sockets on the appropriate
487  * addresses if lockd for user NLM, or perform a lookup of
488  * addresses for the kernel to create transports.
489  *
490  * It gets called one time for each transport.
491  *
492  * It returns 0 upon success, 1 for ingore the call and -1 to indicate
493  * bind failed with EADDRINUSE.
494  *
495  * Any file descriptors that have been created are stored in sock_fd and
496  * the total count of them is maintained in sock_fdcnt.
497  */
498 static int
499 create_service(struct netconfig *nconf)
500 {
501 	struct addrinfo hints, *res = NULL;
502 	struct sockaddr_in *sin;
503 	struct sockaddr_in6 *sin6;
504 	struct __rpc_sockinfo si;
505 	int aicode;
506 	int fd;
507 	int nhostsbak;
508 	int r;
509 	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
510 	int mallocd_res;
511 
512 	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
513 	    (nconf->nc_semantics != NC_TPI_COTS) &&
514 	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
515 		return (1);	/* not my type */
516 
517 	/*
518 	 * XXX - using RPC library internal functions.
519 	 */
520 	if (!__rpc_nconf2sockinfo(nconf, &si)) {
521 		syslog(LOG_ERR, "cannot get information for %s",
522 		    nconf->nc_netid);
523 		return (1);
524 	}
525 
526 	/* Get rpc.statd's address on this transport */
527 	memset(&hints, 0, sizeof hints);
528 	hints.ai_family = si.si_af;
529 	hints.ai_socktype = si.si_socktype;
530 	hints.ai_protocol = si.si_proto;
531 
532 	/*
533 	 * Bind to specific IPs if asked to
534 	 */
535 	nhostsbak = nhosts;
536 	while (nhostsbak > 0) {
537 		--nhostsbak;
538 		mallocd_res = 0;
539 		hints.ai_flags = AI_PASSIVE;
540 
541 		if (!kernel_lockd) {
542 			sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
543 			if (sock_fd == NULL)
544 				out_of_mem();
545 			sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
546 
547 			/*
548 			* XXX - using RPC library internal functions.
549 			*/
550 			if ((fd = __rpc_nconf2fd(nconf)) < 0) {
551 				syslog(LOG_ERR, "cannot create socket for %s",
552 					nconf->nc_netid);
553 				continue;
554 			}
555 		}
556 
557 		switch (hints.ai_family) {
558 			case AF_INET:
559 				if (inet_pton(AF_INET, hosts[nhostsbak],
560 				    host_addr) == 1) {
561 					hints.ai_flags |= AI_NUMERICHOST;
562 				} else {
563 					/*
564 					 * Skip if we have an AF_INET6 address.
565 					 */
566 					if (inet_pton(AF_INET6, hosts[nhostsbak],
567 					    host_addr) == 1) {
568 						if (!kernel_lockd)
569 							close(fd);
570 						continue;
571 					}
572 				}
573 				break;
574 			case AF_INET6:
575 				if (inet_pton(AF_INET6, hosts[nhostsbak],
576 				    host_addr) == 1) {
577 					hints.ai_flags |= AI_NUMERICHOST;
578 				} else {
579 					/*
580 					 * Skip if we have an AF_INET address.
581 					 */
582 					if (inet_pton(AF_INET, hosts[nhostsbak],
583 					    host_addr) == 1) {
584 						if (!kernel_lockd)
585 							close(fd);
586 						continue;
587 					}
588 				}
589 				break;
590 			default:
591 				break;
592 		}
593 
594 		/*
595 		 * If no hosts were specified, just bind to INADDR_ANY
596 		 */
597 		if (strcmp("*", hosts[nhostsbak]) == 0) {
598 			if (svcport_str == NULL) {
599 				if ((res = malloc(sizeof(struct addrinfo))) == NULL)
600 					out_of_mem();
601 				mallocd_res = 1;
602 				res->ai_flags = hints.ai_flags;
603 				res->ai_family = hints.ai_family;
604 				res->ai_protocol = hints.ai_protocol;
605 				switch (res->ai_family) {
606 					case AF_INET:
607 						sin = malloc(sizeof(struct sockaddr_in));
608 						if (sin == NULL)
609 							out_of_mem();
610 						sin->sin_family = AF_INET;
611 						sin->sin_port = htons(0);
612 						sin->sin_addr.s_addr = htonl(INADDR_ANY);
613 						res->ai_addr = (struct sockaddr*) sin;
614 						res->ai_addrlen = (socklen_t)
615 						    sizeof(struct sockaddr_in);
616 						break;
617 					case AF_INET6:
618 						sin6 = malloc(sizeof(struct sockaddr_in6));
619 						if (sin6 == NULL)
620 							out_of_mem();
621 						sin6->sin6_family = AF_INET6;
622 						sin6->sin6_port = htons(0);
623 						sin6->sin6_addr = in6addr_any;
624 						res->ai_addr = (struct sockaddr*) sin6;
625 						res->ai_addrlen = (socklen_t)
626 						    sizeof(struct sockaddr_in6);
627 						break;
628 					default:
629 						syslog(LOG_ERR,
630 						    "bad address family %d",
631 						    res->ai_family);
632 						exit(1);
633 				}
634 			} else {
635 				if ((aicode = getaddrinfo(NULL, svcport_str,
636 				    &hints, &res)) != 0) {
637 					syslog(LOG_ERR,
638 					    "cannot get local address for %s: %s",
639 					    nconf->nc_netid,
640 					    gai_strerror(aicode));
641 					if (!kernel_lockd)
642 						close(fd);
643 					continue;
644 				}
645 			}
646 		} else {
647 			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
648 			    &hints, &res)) != 0) {
649 				syslog(LOG_ERR,
650 				    "cannot get local address for %s: %s",
651 				    nconf->nc_netid, gai_strerror(aicode));
652 				if (!kernel_lockd)
653 					close(fd);
654 				continue;
655 			}
656 		}
657 
658 		if (kernel_lockd) {
659 			struct netbuf servaddr;
660 			char *uaddr;
661 
662 			/*
663 			 * Look up addresses for the kernel to create transports for.
664 			 */
665 			servaddr.len = servaddr.maxlen = res->ai_addrlen;
666 			servaddr.buf = res->ai_addr;
667 			uaddr = taddr2uaddr(nconf, &servaddr);
668 
669 			addrs = realloc(addrs, 2 * (naddrs + 1) * sizeof(char *));
670 			if (!addrs)
671 				out_of_mem();
672 			addrs[2 * naddrs] = strdup(nconf->nc_netid);
673 			addrs[2 * naddrs + 1] = uaddr;
674 			naddrs++;
675 		} else {
676 			/* Store the fd. */
677 			sock_fd[sock_fdcnt - 1] = fd;
678 
679 			/* Now, attempt the bind. */
680 			r = bindresvport_sa(fd, res->ai_addr);
681 			if (r != 0) {
682 				if (errno == EADDRINUSE && mallocd_svcport != 0) {
683 					if (mallocd_res != 0) {
684 						free(res->ai_addr);
685 						free(res);
686 					} else
687 						freeaddrinfo(res);
688 					return (-1);
689 				}
690 				syslog(LOG_ERR, "bindresvport_sa: %m");
691 				exit(1);
692 			}
693 
694 			if (svcport_str == NULL) {
695 				svcport_str = malloc(NI_MAXSERV * sizeof(char));
696 				if (svcport_str == NULL)
697 					out_of_mem();
698 				mallocd_svcport = 1;
699 
700 				if (getnameinfo(res->ai_addr,
701 				res->ai_addr->sa_len, NULL, NI_MAXHOST,
702 				svcport_str, NI_MAXSERV * sizeof(char),
703 				NI_NUMERICHOST | NI_NUMERICSERV))
704 					errx(1, "Cannot get port number");
705 			}
706 		}
707 
708 		if (mallocd_res != 0) {
709 			free(res->ai_addr);
710 			free(res);
711 		} else
712 			freeaddrinfo(res);
713 		res = NULL;
714 	}
715 	return (0);
716 }
717 
718 /*
719  * Called after all the create_service() calls have succeeded, to complete
720  * the setup and registration.
721  */
722 static void
723 complete_service(struct netconfig *nconf, char *port_str)
724 {
725 	struct addrinfo hints, *res = NULL;
726 	struct __rpc_sockinfo si;
727 	struct netbuf servaddr;
728 	SVCXPRT	*transp = NULL;
729 	int aicode, fd, nhostsbak;
730 	int registered = 0;
731 
732 	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
733 	    (nconf->nc_semantics != NC_TPI_COTS) &&
734 	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
735 		return;	/* not my type */
736 
737 	/*
738 	 * XXX - using RPC library internal functions.
739 	 */
740 	if (!__rpc_nconf2sockinfo(nconf, &si)) {
741 		syslog(LOG_ERR, "cannot get information for %s",
742 		    nconf->nc_netid);
743 		return;
744 	}
745 
746 	nhostsbak = nhosts;
747 	while (nhostsbak > 0) {
748 		--nhostsbak;
749 		if (sock_fdpos >= sock_fdcnt) {
750 			/* Should never happen. */
751 			syslog(LOG_ERR, "Ran out of socket fd's");
752 			return;
753 		}
754 		fd = sock_fd[sock_fdpos++];
755 		if (fd < 0)
756 			continue;
757 
758 		if (nconf->nc_semantics != NC_TPI_CLTS)
759 		    listen(fd, SOMAXCONN);
760 
761 		transp = svc_tli_create(fd, nconf, NULL,
762 		    RPC_MAXDATASIZE, RPC_MAXDATASIZE);
763 
764 		if (transp != (SVCXPRT *) NULL) {
765 			if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0,
766 			    NULL))
767 				syslog(LOG_ERR,
768 				    "can't register %s NLM_PROG, NLM_SM service",
769 				    nconf->nc_netid);
770 
771 			if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1,
772 			    NULL))
773 				syslog(LOG_ERR,
774 				    "can't register %s NLM_PROG, NLM_VERS service",
775 				    nconf->nc_netid);
776 
777 			if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3,
778 			    NULL))
779 				syslog(LOG_ERR,
780 				    "can't register %s NLM_PROG, NLM_VERSX service",
781 				    nconf->nc_netid);
782 
783 			if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4,
784 			    NULL))
785 				syslog(LOG_ERR,
786 				    "can't register %s NLM_PROG, NLM_VERS4 service",
787 				    nconf->nc_netid);
788 
789 		} else
790 			syslog(LOG_WARNING, "can't create %s services",
791 			    nconf->nc_netid);
792 
793 		if (registered == 0) {
794 			registered = 1;
795 			memset(&hints, 0, sizeof hints);
796 			hints.ai_flags = AI_PASSIVE;
797 			hints.ai_family = si.si_af;
798 			hints.ai_socktype = si.si_socktype;
799 			hints.ai_protocol = si.si_proto;
800 
801 			if ((aicode = getaddrinfo(NULL, port_str, &hints,
802 			    &res)) != 0) {
803 				syslog(LOG_ERR, "cannot get local address: %s",
804 				    gai_strerror(aicode));
805 				exit(1);
806 			}
807 
808 			servaddr.buf = malloc(res->ai_addrlen);
809 			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
810 			servaddr.len = res->ai_addrlen;
811 
812 			rpcb_set(NLM_PROG, NLM_SM, nconf, &servaddr);
813 			rpcb_set(NLM_PROG, NLM_VERS, nconf, &servaddr);
814 			rpcb_set(NLM_PROG, NLM_VERSX, nconf, &servaddr);
815 			rpcb_set(NLM_PROG, NLM_VERS4, nconf, &servaddr);
816 
817 			xcreated++;
818 			freeaddrinfo(res);
819 		}
820 	} /* end while */
821 }
822 
823 /*
824  * Clear out sockets after a failure to bind one of them, so that the
825  * cycle of socket creation/binding can start anew.
826  */
827 static void
828 clearout_service(void)
829 {
830 	int i;
831 
832 	for (i = 0; i < sock_fdcnt; i++) {
833 		if (sock_fd[i] >= 0) {
834 			shutdown(sock_fd[i], SHUT_RDWR);
835 			close(sock_fd[i]);
836 		}
837 	}
838 }
839 
840 void
841 sigalarm_handler(void)
842 {
843 
844 	grace_expired = 1;
845 }
846 
847 void
848 usage()
849 {
850 	errx(1, "usage: rpc.lockd [-d <debuglevel>]"
851 	    " [-F] [-g <grace period>] [-h <bindip>] [-p <port>]");
852 }
853 
854 /*
855  * init_nsm --
856  *	Reset the NSM state-of-the-world and acquire its state.
857  */
858 void
859 init_nsm(void)
860 {
861 	enum clnt_stat ret;
862 	my_id id;
863 	sm_stat stat;
864 	char name[] = "NFS NLM";
865 
866 	/*
867 	 * !!!
868 	 * The my_id structure isn't used by the SM_UNMON_ALL call, as far
869 	 * as I know.  Leave it empty for now.
870 	 */
871 	memset(&id, 0, sizeof(id));
872 	id.my_name = name;
873 
874 	/*
875 	 * !!!
876 	 * The statd program must already be registered when lockd runs.
877 	 */
878 	do {
879 		ret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON_ALL,
880 		    (xdrproc_t)xdr_my_id, &id, (xdrproc_t)xdr_sm_stat, &stat);
881 		if (ret == RPC_PROGUNAVAIL) {
882 			syslog(LOG_WARNING, "%lu %s", SM_PROG,
883 			    clnt_sperrno(ret));
884 			sleep(2);
885 			continue;
886 		}
887 		break;
888 	} while (0);
889 
890 	if (ret != 0) {
891 		syslog(LOG_ERR, "%lu %s", SM_PROG, clnt_sperrno(ret));
892 		exit(1);
893 	}
894 
895 	nsm_state = stat.state;
896 
897 	/* setup constant data for SM_MON calls */
898 	mon_host.mon_id.my_id.my_name = localhost;
899 	mon_host.mon_id.my_id.my_prog = NLM_PROG;
900 	mon_host.mon_id.my_id.my_vers = NLM_SM;
901 	mon_host.mon_id.my_id.my_proc = NLM_SM_NOTIFY;  /* bsdi addition */
902 }
903 
904 /*
905  * Out of memory, fatal
906  */
907 void out_of_mem()
908 {
909 	syslog(LOG_ERR, "out of memory");
910 	exit(2);
911 }
912