174462Salfred/*	$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $	*/
274462Salfred/*	$FreeBSD: stable/11/usr.sbin/rpc.lockd/lockd.c 355368 2019-12-03 22:54:24Z rpokala $ */
374462Salfred
4331722Seadler/*
514123Speter * Copyright (c) 1995
614123Speter *	A.R. Gordon (andrew.gordon@net-tel.co.uk).  All rights reserved.
714123Speter *
814123Speter * Redistribution and use in source and binary forms, with or without
914123Speter * modification, are permitted provided that the following conditions
1014123Speter * are met:
1114123Speter * 1. Redistributions of source code must retain the above copyright
1214123Speter *    notice, this list of conditions and the following disclaimer.
1314123Speter * 2. Redistributions in binary form must reproduce the above copyright
1414123Speter *    notice, this list of conditions and the following disclaimer in the
1514123Speter *    documentation and/or other materials provided with the distribution.
1614123Speter * 3. All advertising materials mentioning features or use of this software
1714123Speter *    must display the following acknowledgement:
1814123Speter *	This product includes software developed for the FreeBSD project
1914123Speter * 4. Neither the name of the author nor the names of any co-contributors
2014123Speter *    may be used to endorse or promote products derived from this software
2114123Speter *    without specific prior written permission.
2214123Speter *
2314123Speter * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
2414123Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2514123Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2614123Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2714123Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2814123Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2914123Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3014123Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3114123Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3214123Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3314123Speter * SUCH DAMAGE.
3414123Speter *
3514123Speter */
3614123Speter
3774462Salfred#include <sys/cdefs.h>
3830376Scharnier#ifndef lint
3974462Salfred__RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $");
4074462Salfred#endif
4114123Speter
4274462Salfred/*
4374462Salfred * main() function for NFS lock daemon.  Most of the code in this
4474462Salfred * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x.
4574462Salfred *
4674462Salfred * The actual program logic is in the file lock_proc.c
4774462Salfred */
4814123Speter
49177662Sdfr#include <sys/param.h>
50177662Sdfr#include <sys/linker.h>
51177662Sdfr#include <sys/module.h>
5274462Salfred#include <sys/socket.h>
53177633Sdfr#include <sys/stat.h>
5474462Salfred
55173281Smatteo#include <netinet/in.h>
56173281Smatteo#include <arpa/inet.h>
57173281Smatteo
5830376Scharnier#include <err.h>
5974462Salfred#include <stdio.h>
6030376Scharnier#include <stdlib.h>
6174462Salfred#include <errno.h>
6274462Salfred#include <syslog.h>
6374462Salfred#include <signal.h>
6430376Scharnier#include <string.h>
6574462Salfred#include <unistd.h>
6674462Salfred#include <libutil.h>
6774462Salfred#include <netconfig.h>
68173281Smatteo#include <netdb.h>
6974462Salfred
7030376Scharnier#include <rpc/rpc.h>
71109363Smbr#include <rpc/rpc_com.h>
7274462Salfred#include <rpcsvc/sm_inter.h>
7374462Salfred
7414123Speter#include "lockd.h"
7574462Salfred#include <rpcsvc/nlm_prot.h>
7614123Speter
77222624Srmacklem#define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
78222624Srmacklem
7974462Salfredint		debug_level = 0;	/* 0 = no debugging syslog() calls */
8074462Salfredint		_rpcsvcdirty = 0;
8114123Speter
8274462Salfredint grace_expired;
8375631Salfredint nsm_state;
84177633Sdfrint kernel_lockd;
85180025Sdfrint kernel_lockd_client;
8675631Salfredpid_t client_pid;
8775631Salfredstruct mon mon_host;
88173281Smatteochar **hosts, *svcport_str = NULL;
89222624Srmacklemstatic int	mallocd_svcport = 0;
90222624Srmacklemstatic int	*sock_fd;
91222624Srmacklemstatic int	sock_fdcnt;
92222624Srmacklemstatic int	sock_fdpos;
93173281Smatteoint nhosts = 0;
94173281Smatteoint xcreated = 0;
95177633Sdfrchar **addrs;			/* actually (netid, uaddr) pairs */
96177633Sdfrint naddrs;			/* count of how many (netid, uaddr) pairs */
97216603Suqschar localhost[] = "localhost";
9814123Speter
99222624Srmacklemstatic int	create_service(struct netconfig *nconf);
100222624Srmacklemstatic void	complete_service(struct netconfig *nconf, char *port_str);
101222624Srmacklemstatic void	clearout_service(void);
102320217Sdelphijstatic void	out_of_mem(void) __dead2;
10375631Salfredvoid	init_nsm(void);
10492909Salfredvoid	usage(void);
10574462Salfred
10692909Salfredvoid sigalarm_handler(void);
10774462Salfred
108177633Sdfr/*
109177633Sdfr * XXX move to some header file.
110177633Sdfr */
111177633Sdfr#define _PATH_RPCLOCKDSOCK	"/var/run/rpclockd.sock"
112177633Sdfr
11330376Scharnierint
114173281Smatteomain(int argc, char **argv)
11514123Speter{
116173281Smatteo	int ch, i, s;
117173281Smatteo	void *nc_handle;
118173281Smatteo	char *endptr, **hosts_bak;
11987096Salfred	struct sigaction sigalarm;
12074462Salfred	int grace_period = 30;
121355368Srpokala	int foreground = 0;
12274462Salfred	struct netconfig *nconf;
123173281Smatteo	int have_v6 = 1;
124109363Smbr	int maxrec = RPC_MAXDATASIZE;
125168324Smatteo	in_port_t svcport = 0;
126222624Srmacklem	int attempt_cnt, port_len, port_pos, ret;
127222624Srmacklem	char **port_list;
12814123Speter
129355368Srpokala	while ((ch = getopt(argc, argv, "d:Fg:h:p:")) != (-1)) {
13074462Salfred		switch (ch) {
13174462Salfred		case 'd':
13274462Salfred			debug_level = atoi(optarg);
13374462Salfred			if (!debug_level) {
13474462Salfred				usage();
13574462Salfred				/* NOTREACHED */
13674462Salfred			}
13774462Salfred			break;
138355368Srpokala		case 'F':
139355368Srpokala			foreground = 1;
140355368Srpokala			break;
14174462Salfred		case 'g':
14274462Salfred			grace_period = atoi(optarg);
14374462Salfred			if (!grace_period) {
14474462Salfred				usage();
14574462Salfred				/* NOTREACHED */
14674462Salfred			}
14774462Salfred			break;
148173281Smatteo		case 'h':
149173281Smatteo			++nhosts;
150173281Smatteo			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
151173281Smatteo			if (hosts_bak == NULL) {
152173281Smatteo				if (hosts != NULL) {
153173281Smatteo					for (i = 0; i < nhosts; i++)
154173281Smatteo						free(hosts[i]);
155173281Smatteo					free(hosts);
156173281Smatteo					out_of_mem();
157173281Smatteo				}
158173281Smatteo			}
159173281Smatteo			hosts = hosts_bak;
160173281Smatteo			hosts[nhosts - 1] = strdup(optarg);
161173281Smatteo			if (hosts[nhosts - 1] == NULL) {
162173281Smatteo				for (i = 0; i < (nhosts - 1); i++)
163173281Smatteo					free(hosts[i]);
164173281Smatteo				free(hosts);
165173281Smatteo				out_of_mem();
166173281Smatteo			}
167173281Smatteo			break;
168168324Smatteo		case 'p':
169168324Smatteo			endptr = NULL;
170168324Smatteo			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
171168324Smatteo			if (endptr == NULL || *endptr != '\0' ||
172168324Smatteo			    svcport == 0 || svcport >= IPPORT_MAX)
173168324Smatteo				usage();
174173281Smatteo			svcport_str = strdup(optarg);
175168324Smatteo			break;
17674462Salfred		default:
17774462Salfred			usage();
17874462Salfred			/* NOTREACHED */
17974462Salfred		}
18074462Salfred	}
18174462Salfred	if (geteuid()) { /* This command allowed only to root */
18274462Salfred		fprintf(stderr, "Sorry. You are not superuser\n");
18374462Salfred		exit(1);
18474462Salfred        }
18514123Speter
186177662Sdfr	kernel_lockd = FALSE;
187180025Sdfr	kernel_lockd_client = FALSE;
188177662Sdfr	if (modfind("nfslockd") < 0) {
189177662Sdfr		if (kldload("nfslockd") < 0) {
190177662Sdfr			fprintf(stderr, "Can't find or load kernel support for rpc.lockd - using non-kernel implementation\n");
191178066Sdfr		} else {
192178066Sdfr			kernel_lockd = TRUE;
193177662Sdfr		}
194177662Sdfr	} else {
195177662Sdfr		kernel_lockd = TRUE;
196177662Sdfr	}
197180025Sdfr	if (kernel_lockd) {
198180025Sdfr		if (getosreldate() >= 800040)
199180025Sdfr			kernel_lockd_client = TRUE;
200180025Sdfr	}
201177662Sdfr
20274462Salfred	(void)rpcb_unset(NLM_PROG, NLM_SM, NULL);
20374462Salfred	(void)rpcb_unset(NLM_PROG, NLM_VERS, NULL);
20474462Salfred	(void)rpcb_unset(NLM_PROG, NLM_VERSX, NULL);
20574462Salfred	(void)rpcb_unset(NLM_PROG, NLM_VERS4, NULL);
20614123Speter
20774462Salfred	/*
20874462Salfred	 * Check if IPv6 support is present.
20974462Salfred	 */
21074462Salfred	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
21174462Salfred	if (s < 0)
212173281Smatteo		have_v6 = 0;
213173281Smatteo	else
21474462Salfred		close(s);
21514123Speter
216173281Smatteo	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
217168324Smatteo
218173281Smatteo	/*
219173281Smatteo	 * If no hosts were specified, add a wildcard entry to bind to
220173281Smatteo	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
221173281Smatteo	 * list.
222173281Smatteo	 */
223173281Smatteo	if (nhosts == 0) {
224292864Suqs		hosts = malloc(sizeof(char *));
225173281Smatteo		if (hosts == NULL)
226173281Smatteo			out_of_mem();
227168324Smatteo
228260251Sdelphij		hosts[0] = strdup("*");
229173281Smatteo		nhosts = 1;
230173281Smatteo	} else {
231173281Smatteo		if (have_v6) {
232173281Smatteo			hosts_bak = realloc(hosts, (nhosts + 2) *
233173281Smatteo			    sizeof(char *));
234173281Smatteo			if (hosts_bak == NULL) {
235173281Smatteo				for (i = 0; i < nhosts; i++)
236173281Smatteo					free(hosts[i]);
237173281Smatteo				free(hosts);
238173281Smatteo				out_of_mem();
239173281Smatteo			} else
240173281Smatteo				hosts = hosts_bak;
241109363Smbr
242173281Smatteo			nhosts += 2;
243260251Sdelphij			hosts[nhosts - 2] = strdup("::1");
244173281Smatteo		} else {
245173281Smatteo			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
246173281Smatteo			if (hosts_bak == NULL) {
247173281Smatteo				for (i = 0; i < nhosts; i++)
248173281Smatteo					free(hosts[i]);
24914123Speter
250173281Smatteo				free(hosts);
251173281Smatteo				out_of_mem();
252173281Smatteo			} else {
253173281Smatteo				nhosts += 1;
254173281Smatteo				hosts = hosts_bak;
255168324Smatteo			}
256168324Smatteo		}
257260251Sdelphij		hosts[nhosts - 1] = strdup("127.0.0.1");
258173281Smatteo	}
259168324Smatteo
260177633Sdfr	if (kernel_lockd) {
261180025Sdfr		if (!kernel_lockd_client) {
262180025Sdfr			/*
263180025Sdfr			 * For the case where we have a kernel lockd but it
264180025Sdfr			 * doesn't provide client locking, we run a cut-down
265180025Sdfr			 * RPC service on a local-domain socket. The kernel's
266180025Sdfr			 * RPC server will pass what it can't handle (mainly
267180025Sdfr			 * client replies) down to us.
268180025Sdfr			 */
269180025Sdfr			struct sockaddr_un sun;
270180025Sdfr			int fd, oldmask;
271180025Sdfr			SVCXPRT *xprt;
272177633Sdfr
273180025Sdfr			memset(&sun, 0, sizeof sun);
274180025Sdfr			sun.sun_family = AF_LOCAL;
275180025Sdfr			unlink(_PATH_RPCLOCKDSOCK);
276180025Sdfr			strcpy(sun.sun_path, _PATH_RPCLOCKDSOCK);
277180025Sdfr			sun.sun_len = SUN_LEN(&sun);
278180025Sdfr			fd = socket(AF_LOCAL, SOCK_STREAM, 0);
279180025Sdfr			if (!fd) {
280180025Sdfr				err(1, "Can't create local lockd socket");
281180025Sdfr			}
282180025Sdfr			oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
283180025Sdfr			if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) {
284180025Sdfr				err(1, "Can't bind local lockd socket");
285180025Sdfr			}
286180025Sdfr			umask(oldmask);
287180025Sdfr			if (listen(fd, SOMAXCONN) < 0) {
288180025Sdfr				err(1, "Can't listen on local lockd socket");
289180025Sdfr			}
290180025Sdfr			xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
291180025Sdfr			if (!xprt) {
292180025Sdfr				err(1, "Can't create transport for local lockd socket");
293180025Sdfr			}
294180025Sdfr			if (!svc_reg(xprt, NLM_PROG, NLM_VERS4, nlm_prog_4, NULL)) {
295180025Sdfr				err(1, "Can't register service for local lockd socket");
296180025Sdfr			}
297177633Sdfr		}
298177633Sdfr
299177633Sdfr		/*
300177633Sdfr		 * We need to look up the addresses so that we can
301177633Sdfr		 * hand uaddrs (ascii encoded address+port strings) to
302177633Sdfr		 * the kernel.
303177633Sdfr		 */
304177633Sdfr		nc_handle = setnetconfig();
305177633Sdfr		while ((nconf = getnetconfig(nc_handle))) {
306177633Sdfr			/* We want to listen only on udp6, tcp6, udp, tcp transports */
307177633Sdfr			if (nconf->nc_flag & NC_VISIBLE) {
308177633Sdfr				/* Skip if there's no IPv6 support */
309177633Sdfr				if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
310177633Sdfr					/* DO NOTHING */
311177633Sdfr				} else {
312320217Sdelphij					create_service(nconf);
313177633Sdfr				}
314173281Smatteo			}
31574462Salfred		}
316177633Sdfr		endnetconfig(nc_handle);
317177633Sdfr	} else {
318222624Srmacklem		attempt_cnt = 1;
319222624Srmacklem		sock_fdcnt = 0;
320222624Srmacklem		sock_fd = NULL;
321222624Srmacklem		port_list = NULL;
322222624Srmacklem		port_len = 0;
323177633Sdfr		nc_handle = setnetconfig();
324177633Sdfr		while ((nconf = getnetconfig(nc_handle))) {
325177633Sdfr			/* We want to listen only on udp6, tcp6, udp, tcp transports */
326177633Sdfr			if (nconf->nc_flag & NC_VISIBLE) {
327177633Sdfr				/* Skip if there's no IPv6 support */
328177633Sdfr				if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
329177633Sdfr					/* DO NOTHING */
330177633Sdfr				} else {
331222624Srmacklem					ret = create_service(nconf);
332222624Srmacklem					if (ret == 1)
333222624Srmacklem						/* Ignore this call */
334222624Srmacklem						continue;
335222624Srmacklem					if (ret < 0) {
336222624Srmacklem						/*
337222624Srmacklem						 * Failed to bind port, so close
338222624Srmacklem						 * off all sockets created and
339222624Srmacklem						 * try again if the port# was
340222624Srmacklem						 * dynamically assigned via
341222624Srmacklem						 * bind(2).
342222624Srmacklem						 */
343222624Srmacklem						clearout_service();
344222624Srmacklem						if (mallocd_svcport != 0 &&
345222624Srmacklem						    attempt_cnt <
346222624Srmacklem						    GETPORT_MAXTRY) {
347222624Srmacklem							free(svcport_str);
348222624Srmacklem							svcport_str = NULL;
349222624Srmacklem							mallocd_svcport = 0;
350222624Srmacklem						} else {
351222624Srmacklem							errno = EADDRINUSE;
352222624Srmacklem							syslog(LOG_ERR,
353222624Srmacklem							 "bindresvport_sa: %m");
354222624Srmacklem							exit(1);
355222624Srmacklem						}
356222624Srmacklem
357222624Srmacklem						/*
358222624Srmacklem						 * Start over at the first
359222624Srmacklem						 * service.
360222624Srmacklem						 */
361222624Srmacklem						free(sock_fd);
362222624Srmacklem						sock_fdcnt = 0;
363222624Srmacklem						sock_fd = NULL;
364222624Srmacklem						nc_handle = setnetconfig();
365222624Srmacklem						attempt_cnt++;
366222624Srmacklem					} else if (mallocd_svcport != 0 &&
367222624Srmacklem					    attempt_cnt == GETPORT_MAXTRY) {
368222624Srmacklem						/*
369222624Srmacklem						 * For the last attempt, allow
370222624Srmacklem						 * different port #s for each
371222624Srmacklem						 * nconf by saving the
372222624Srmacklem						 * svcport_str and setting it
373222624Srmacklem						 * back to NULL.
374222624Srmacklem						 */
375222624Srmacklem						port_list = realloc(port_list,
376222624Srmacklem						    (port_len + 1) *
377222624Srmacklem						    sizeof(char *));
378222624Srmacklem						if (port_list == NULL)
379222624Srmacklem							out_of_mem();
380222624Srmacklem						port_list[port_len++] =
381222624Srmacklem						    svcport_str;
382222624Srmacklem						svcport_str = NULL;
383222624Srmacklem						mallocd_svcport = 0;
384222624Srmacklem					}
385177633Sdfr				}
386177633Sdfr			}
387177633Sdfr		}
388222624Srmacklem
389222624Srmacklem		/*
390222624Srmacklem		 * Successfully bound the ports, so call complete_service() to
391222624Srmacklem		 * do the rest of the setup on the service(s).
392222624Srmacklem		 */
393222624Srmacklem		sock_fdpos = 0;
394222624Srmacklem		port_pos = 0;
395222624Srmacklem		nc_handle = setnetconfig();
396222624Srmacklem		while ((nconf = getnetconfig(nc_handle))) {
397222624Srmacklem			/* We want to listen only on udp6, tcp6, udp, tcp transports */
398222624Srmacklem			if (nconf->nc_flag & NC_VISIBLE) {
399222624Srmacklem				/* Skip if there's no IPv6 support */
400222624Srmacklem				if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
401222624Srmacklem					/* DO NOTHING */
402222624Srmacklem				} else if (port_list != NULL) {
403222624Srmacklem					if (port_pos >= port_len) {
404222624Srmacklem						syslog(LOG_ERR,
405222624Srmacklem						    "too many port#s");
406222624Srmacklem						exit(1);
407222624Srmacklem					}
408222624Srmacklem					complete_service(nconf,
409222624Srmacklem					    port_list[port_pos++]);
410222624Srmacklem				} else
411222624Srmacklem					complete_service(nconf, svcport_str);
412222624Srmacklem			}
413222624Srmacklem		}
414177633Sdfr		endnetconfig(nc_handle);
415222624Srmacklem		free(sock_fd);
416222624Srmacklem		if (port_list != NULL) {
417222624Srmacklem			for (port_pos = 0; port_pos < port_len; port_pos++)
418222624Srmacklem				free(port_list[port_pos]);
419222624Srmacklem			free(port_list);
420222624Srmacklem		}
42174462Salfred	}
42214123Speter
42374462Salfred	/*
42474462Salfred	 * Note that it is NOT sensible to run this program from inetd - the
42574462Salfred	 * protocol assumes that it will run immediately at boot time.
42674462Salfred	 */
427355368Srpokala	if ((foreground == 0) && daemon(0, debug_level > 0)) {
42874462Salfred		err(1, "cannot fork");
42974462Salfred		/* NOTREACHED */
43074462Salfred	}
43174462Salfred
43274462Salfred	openlog("rpc.lockd", 0, LOG_DAEMON);
43374462Salfred	if (debug_level)
43474462Salfred		syslog(LOG_INFO, "Starting, debug level %d", debug_level);
43574462Salfred	else
43674462Salfred		syslog(LOG_INFO, "Starting");
43774462Salfred
43886319Salfred	sigalarm.sa_handler = (sig_t) sigalarm_handler;
43974462Salfred	sigemptyset(&sigalarm.sa_mask);
44074462Salfred	sigalarm.sa_flags = SA_RESETHAND; /* should only happen once */
44174462Salfred	sigalarm.sa_flags |= SA_RESTART;
44274462Salfred	if (sigaction(SIGALRM, &sigalarm, NULL) != 0) {
44374462Salfred		syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s",
44474462Salfred		    strerror(errno));
44574462Salfred		exit(1);
44674462Salfred	}
44774462Salfred
448177633Sdfr	if (kernel_lockd) {
449180025Sdfr		if (!kernel_lockd_client) {
450180025Sdfr			init_nsm();
451180025Sdfr			client_pid = client_request();
45275631Salfred
453180025Sdfr			/*
454180025Sdfr			 * Create a child process to enter the kernel and then
455180025Sdfr			 * wait for RPCs on our local domain socket.
456180025Sdfr			 */
457180025Sdfr			if (!fork())
458180025Sdfr				nlm_syscall(debug_level, grace_period,
459180025Sdfr				    naddrs, addrs);
460180025Sdfr			else
461180025Sdfr				svc_run();
462180025Sdfr		} else {
463180025Sdfr			/*
464180025Sdfr			 * The kernel lockd implementation provides
465180025Sdfr			 * both client and server so we don't need to
466180025Sdfr			 * do anything else.
467180025Sdfr			 */
468177633Sdfr			nlm_syscall(debug_level, grace_period, naddrs, addrs);
469180025Sdfr		}
470177633Sdfr	} else {
471177633Sdfr		grace_expired = 0;
472177633Sdfr		alarm(grace_period);
47375631Salfred
474177633Sdfr		init_nsm();
475177633Sdfr
476177633Sdfr		client_pid = client_request();
477177633Sdfr
478177633Sdfr		svc_run();		/* Should never return */
479177633Sdfr	}
48074462Salfred	exit(1);
48114123Speter}
48230376Scharnier
483173281Smatteo/*
484173281Smatteo * This routine creates and binds sockets on the appropriate
485320217Sdelphij * addresses if lockd for user NLM, or perform a lookup of
486320217Sdelphij * addresses for the kernel to create transports.
487320217Sdelphij *
488320217Sdelphij * It gets called one time for each transport.
489320217Sdelphij *
490222624Srmacklem * It returns 0 upon success, 1 for ingore the call and -1 to indicate
491222624Srmacklem * bind failed with EADDRINUSE.
492320217Sdelphij *
493222624Srmacklem * Any file descriptors that have been created are stored in sock_fd and
494222624Srmacklem * the total count of them is maintained in sock_fdcnt.
495173281Smatteo */
496222624Srmacklemstatic int
497173281Smatteocreate_service(struct netconfig *nconf)
498173281Smatteo{
499173281Smatteo	struct addrinfo hints, *res = NULL;
500173281Smatteo	struct sockaddr_in *sin;
501173281Smatteo	struct sockaddr_in6 *sin6;
502173281Smatteo	struct __rpc_sockinfo si;
503173281Smatteo	int aicode;
504173281Smatteo	int fd;
505173281Smatteo	int nhostsbak;
506173281Smatteo	int r;
507173281Smatteo	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
508222624Srmacklem	int mallocd_res;
509173281Smatteo
510173281Smatteo	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
511173281Smatteo	    (nconf->nc_semantics != NC_TPI_COTS) &&
512173281Smatteo	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
513222624Srmacklem		return (1);	/* not my type */
514173281Smatteo
515173281Smatteo	/*
516173281Smatteo	 * XXX - using RPC library internal functions.
517173281Smatteo	 */
518173281Smatteo	if (!__rpc_nconf2sockinfo(nconf, &si)) {
519173281Smatteo		syslog(LOG_ERR, "cannot get information for %s",
520173281Smatteo		    nconf->nc_netid);
521222624Srmacklem		return (1);
522173281Smatteo	}
523173281Smatteo
524173281Smatteo	/* Get rpc.statd's address on this transport */
525173281Smatteo	memset(&hints, 0, sizeof hints);
526173281Smatteo	hints.ai_family = si.si_af;
527173281Smatteo	hints.ai_socktype = si.si_socktype;
528173281Smatteo	hints.ai_protocol = si.si_proto;
529173281Smatteo
530173281Smatteo	/*
531173281Smatteo	 * Bind to specific IPs if asked to
532173281Smatteo	 */
533173281Smatteo	nhostsbak = nhosts;
534173281Smatteo	while (nhostsbak > 0) {
535173281Smatteo		--nhostsbak;
536222624Srmacklem		mallocd_res = 0;
537277352Srstone		hints.ai_flags = AI_PASSIVE;
538173281Smatteo
539320217Sdelphij		if (!kernel_lockd) {
540320217Sdelphij			sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
541320217Sdelphij			if (sock_fd == NULL)
542320217Sdelphij				out_of_mem();
543320217Sdelphij			sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
544320217Sdelphij
545320217Sdelphij			/*
546320217Sdelphij			* XXX - using RPC library internal functions.
547320217Sdelphij			*/
548320217Sdelphij			if ((fd = __rpc_nconf2fd(nconf)) < 0) {
549320217Sdelphij				syslog(LOG_ERR, "cannot create socket for %s",
550320217Sdelphij					nconf->nc_netid);
551320217Sdelphij				continue;
552320217Sdelphij			}
553173281Smatteo		}
554173281Smatteo
555173281Smatteo		switch (hints.ai_family) {
556173281Smatteo			case AF_INET:
557173281Smatteo				if (inet_pton(AF_INET, hosts[nhostsbak],
558173281Smatteo				    host_addr) == 1) {
559222624Srmacklem					hints.ai_flags |= AI_NUMERICHOST;
560173281Smatteo				} else {
561173281Smatteo					/*
562173281Smatteo					 * Skip if we have an AF_INET6 address.
563173281Smatteo					 */
564173281Smatteo					if (inet_pton(AF_INET6, hosts[nhostsbak],
565173281Smatteo					    host_addr) == 1) {
566320217Sdelphij						if (!kernel_lockd)
567320217Sdelphij							close(fd);
568173281Smatteo						continue;
569173281Smatteo					}
570173281Smatteo				}
571173281Smatteo				break;
572173281Smatteo			case AF_INET6:
573173281Smatteo				if (inet_pton(AF_INET6, hosts[nhostsbak],
574173281Smatteo				    host_addr) == 1) {
575222624Srmacklem					hints.ai_flags |= AI_NUMERICHOST;
576173281Smatteo				} else {
577173281Smatteo					/*
578173281Smatteo					 * Skip if we have an AF_INET address.
579173281Smatteo					 */
580173281Smatteo					if (inet_pton(AF_INET, hosts[nhostsbak],
581173281Smatteo					    host_addr) == 1) {
582320217Sdelphij						if (!kernel_lockd)
583320217Sdelphij							close(fd);
584173281Smatteo						continue;
585173281Smatteo					}
586173281Smatteo				}
587173281Smatteo				break;
588173281Smatteo			default:
589173281Smatteo				break;
590173281Smatteo		}
591173281Smatteo
592173281Smatteo		/*
593173281Smatteo		 * If no hosts were specified, just bind to INADDR_ANY
594173281Smatteo		 */
595173281Smatteo		if (strcmp("*", hosts[nhostsbak]) == 0) {
596173281Smatteo			if (svcport_str == NULL) {
597320217Sdelphij				if ((res = malloc(sizeof(struct addrinfo))) == NULL)
598173281Smatteo					out_of_mem();
599222624Srmacklem				mallocd_res = 1;
600173281Smatteo				res->ai_flags = hints.ai_flags;
601173281Smatteo				res->ai_family = hints.ai_family;
602173281Smatteo				res->ai_protocol = hints.ai_protocol;
603173281Smatteo				switch (res->ai_family) {
604173281Smatteo					case AF_INET:
605173281Smatteo						sin = malloc(sizeof(struct sockaddr_in));
606173281Smatteo						if (sin == NULL)
607173281Smatteo							out_of_mem();
608173281Smatteo						sin->sin_family = AF_INET;
609173281Smatteo						sin->sin_port = htons(0);
610173281Smatteo						sin->sin_addr.s_addr = htonl(INADDR_ANY);
611173281Smatteo						res->ai_addr = (struct sockaddr*) sin;
612173281Smatteo						res->ai_addrlen = (socklen_t)
613222624Srmacklem						    sizeof(struct sockaddr_in);
614173281Smatteo						break;
615173281Smatteo					case AF_INET6:
616173281Smatteo						sin6 = malloc(sizeof(struct sockaddr_in6));
617173411Smatteo						if (sin6 == NULL)
618173281Smatteo							out_of_mem();
619173281Smatteo						sin6->sin6_family = AF_INET6;
620173281Smatteo						sin6->sin6_port = htons(0);
621173281Smatteo						sin6->sin6_addr = in6addr_any;
622173281Smatteo						res->ai_addr = (struct sockaddr*) sin6;
623222624Srmacklem						res->ai_addrlen = (socklen_t)
624222624Srmacklem						    sizeof(struct sockaddr_in6);
625173281Smatteo						break;
626173281Smatteo					default:
627222624Srmacklem						syslog(LOG_ERR,
628320217Sdelphij						    "bad address family %d",
629222624Srmacklem						    res->ai_family);
630222624Srmacklem						exit(1);
631173281Smatteo				}
632173281Smatteo			} else {
633173281Smatteo				if ((aicode = getaddrinfo(NULL, svcport_str,
634173281Smatteo				    &hints, &res)) != 0) {
635173281Smatteo					syslog(LOG_ERR,
636173281Smatteo					    "cannot get local address for %s: %s",
637173281Smatteo					    nconf->nc_netid,
638173281Smatteo					    gai_strerror(aicode));
639320217Sdelphij					if (!kernel_lockd)
640320217Sdelphij						close(fd);
641173281Smatteo					continue;
642173281Smatteo				}
643173281Smatteo			}
644173281Smatteo		} else {
645173281Smatteo			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
646173281Smatteo			    &hints, &res)) != 0) {
647173281Smatteo				syslog(LOG_ERR,
648173281Smatteo				    "cannot get local address for %s: %s",
649173281Smatteo				    nconf->nc_netid, gai_strerror(aicode));
650320217Sdelphij				if (!kernel_lockd)
651320217Sdelphij					close(fd);
652173281Smatteo				continue;
653173281Smatteo			}
654173281Smatteo		}
655173281Smatteo
656320217Sdelphij		if (kernel_lockd) {
657320217Sdelphij			struct netbuf servaddr;
658320217Sdelphij			char *uaddr;
659222624Srmacklem
660320217Sdelphij			/*
661320217Sdelphij			 * Look up addresses for the kernel to create transports for.
662320217Sdelphij			 */
663320217Sdelphij			servaddr.len = servaddr.maxlen = res->ai_addrlen;
664320217Sdelphij			servaddr.buf = res->ai_addr;
665320217Sdelphij			uaddr = taddr2uaddr(nconf, &servaddr);
666222624Srmacklem
667320217Sdelphij			addrs = realloc(addrs, 2 * (naddrs + 1) * sizeof(char *));
668320217Sdelphij			if (!addrs)
669320217Sdelphij				out_of_mem();
670320217Sdelphij			addrs[2 * naddrs] = strdup(nconf->nc_netid);
671320217Sdelphij			addrs[2 * naddrs + 1] = uaddr;
672320217Sdelphij			naddrs++;
673320217Sdelphij		} else {
674320217Sdelphij			/* Store the fd. */
675320217Sdelphij			sock_fd[sock_fdcnt - 1] = fd;
676320217Sdelphij
677320217Sdelphij			/* Now, attempt the bind. */
678320217Sdelphij			r = bindresvport_sa(fd, res->ai_addr);
679320217Sdelphij			if (r != 0) {
680320217Sdelphij				if (errno == EADDRINUSE && mallocd_svcport != 0) {
681320217Sdelphij					if (mallocd_res != 0) {
682320217Sdelphij						free(res->ai_addr);
683320217Sdelphij						free(res);
684320217Sdelphij					} else
685320217Sdelphij						freeaddrinfo(res);
686320217Sdelphij					return (-1);
687320217Sdelphij				}
688320217Sdelphij				syslog(LOG_ERR, "bindresvport_sa: %m");
689320217Sdelphij				exit(1);
690222624Srmacklem			}
691173281Smatteo
692320217Sdelphij			if (svcport_str == NULL) {
693320217Sdelphij				svcport_str = malloc(NI_MAXSERV * sizeof(char));
694320217Sdelphij				if (svcport_str == NULL)
695320217Sdelphij					out_of_mem();
696320217Sdelphij				mallocd_svcport = 1;
697222624Srmacklem
698320217Sdelphij				if (getnameinfo(res->ai_addr,
699320217Sdelphij				res->ai_addr->sa_len, NULL, NI_MAXHOST,
700320217Sdelphij				svcport_str, NI_MAXSERV * sizeof(char),
701320217Sdelphij				NI_NUMERICHOST | NI_NUMERICSERV))
702320217Sdelphij					errx(1, "Cannot get port number");
703320217Sdelphij			}
704222624Srmacklem		}
705320217Sdelphij
706222624Srmacklem		if (mallocd_res != 0) {
707222624Srmacklem			free(res->ai_addr);
708222624Srmacklem			free(res);
709222624Srmacklem		} else
710222624Srmacklem			freeaddrinfo(res);
711222624Srmacklem		res = NULL;
712222624Srmacklem	}
713222624Srmacklem	return (0);
714222624Srmacklem}
715222624Srmacklem
716222624Srmacklem/*
717222624Srmacklem * Called after all the create_service() calls have succeeded, to complete
718222624Srmacklem * the setup and registration.
719222624Srmacklem */
720222624Srmacklemstatic void
721222624Srmacklemcomplete_service(struct netconfig *nconf, char *port_str)
722222624Srmacklem{
723222624Srmacklem	struct addrinfo hints, *res = NULL;
724222624Srmacklem	struct __rpc_sockinfo si;
725222624Srmacklem	struct netbuf servaddr;
726222624Srmacklem	SVCXPRT	*transp = NULL;
727222624Srmacklem	int aicode, fd, nhostsbak;
728222624Srmacklem	int registered = 0;
729222624Srmacklem
730222624Srmacklem	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
731222624Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS) &&
732222624Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
733222624Srmacklem		return;	/* not my type */
734222624Srmacklem
735222624Srmacklem	/*
736222624Srmacklem	 * XXX - using RPC library internal functions.
737222624Srmacklem	 */
738222624Srmacklem	if (!__rpc_nconf2sockinfo(nconf, &si)) {
739222624Srmacklem		syslog(LOG_ERR, "cannot get information for %s",
740222624Srmacklem		    nconf->nc_netid);
741222624Srmacklem		return;
742222624Srmacklem	}
743222624Srmacklem
744222624Srmacklem	nhostsbak = nhosts;
745222624Srmacklem	while (nhostsbak > 0) {
746222624Srmacklem		--nhostsbak;
747222624Srmacklem		if (sock_fdpos >= sock_fdcnt) {
748222624Srmacklem			/* Should never happen. */
749222624Srmacklem			syslog(LOG_ERR, "Ran out of socket fd's");
750222624Srmacklem			return;
751222624Srmacklem		}
752222624Srmacklem		fd = sock_fd[sock_fdpos++];
753222624Srmacklem		if (fd < 0)
754222624Srmacklem			continue;
755222624Srmacklem
756177950Sdfr		if (nconf->nc_semantics != NC_TPI_CLTS)
757177950Sdfr		    listen(fd, SOMAXCONN);
758177950Sdfr
759173281Smatteo		transp = svc_tli_create(fd, nconf, NULL,
760173281Smatteo		    RPC_MAXDATASIZE, RPC_MAXDATASIZE);
761173281Smatteo
762173281Smatteo		if (transp != (SVCXPRT *) NULL) {
763173281Smatteo			if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0,
764173281Smatteo			    NULL))
765173281Smatteo				syslog(LOG_ERR,
766173281Smatteo				    "can't register %s NLM_PROG, NLM_SM service",
767173281Smatteo				    nconf->nc_netid);
768173281Smatteo
769173281Smatteo			if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1,
770173281Smatteo			    NULL))
771173281Smatteo				syslog(LOG_ERR,
772173281Smatteo				    "can't register %s NLM_PROG, NLM_VERS service",
773173281Smatteo				    nconf->nc_netid);
774173281Smatteo
775173281Smatteo			if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3,
776173281Smatteo			    NULL))
777173281Smatteo				syslog(LOG_ERR,
778173281Smatteo				    "can't register %s NLM_PROG, NLM_VERSX service",
779173281Smatteo				    nconf->nc_netid);
780173281Smatteo
781173281Smatteo			if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4,
782173281Smatteo			    NULL))
783173281Smatteo				syslog(LOG_ERR,
784173281Smatteo				    "can't register %s NLM_PROG, NLM_VERS4 service",
785173281Smatteo				    nconf->nc_netid);
786173281Smatteo
787173281Smatteo		} else
788173281Smatteo			syslog(LOG_WARNING, "can't create %s services",
789173281Smatteo			    nconf->nc_netid);
790173281Smatteo
791173281Smatteo		if (registered == 0) {
792173281Smatteo			registered = 1;
793173281Smatteo			memset(&hints, 0, sizeof hints);
794173281Smatteo			hints.ai_flags = AI_PASSIVE;
795173281Smatteo			hints.ai_family = si.si_af;
796173281Smatteo			hints.ai_socktype = si.si_socktype;
797173281Smatteo			hints.ai_protocol = si.si_proto;
798173281Smatteo
799222624Srmacklem			if ((aicode = getaddrinfo(NULL, port_str, &hints,
800173281Smatteo			    &res)) != 0) {
801173281Smatteo				syslog(LOG_ERR, "cannot get local address: %s",
802173281Smatteo				    gai_strerror(aicode));
803173281Smatteo				exit(1);
804173281Smatteo			}
805173281Smatteo
806173281Smatteo			servaddr.buf = malloc(res->ai_addrlen);
807173281Smatteo			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
808173281Smatteo			servaddr.len = res->ai_addrlen;
809173281Smatteo
810173281Smatteo			rpcb_set(NLM_PROG, NLM_SM, nconf, &servaddr);
811173281Smatteo			rpcb_set(NLM_PROG, NLM_VERS, nconf, &servaddr);
812173281Smatteo			rpcb_set(NLM_PROG, NLM_VERSX, nconf, &servaddr);
813173281Smatteo			rpcb_set(NLM_PROG, NLM_VERS4, nconf, &servaddr);
814173281Smatteo
815173281Smatteo			xcreated++;
816173281Smatteo			freeaddrinfo(res);
817173281Smatteo		}
818173281Smatteo	} /* end while */
819173281Smatteo}
820173281Smatteo
821177633Sdfr/*
822222624Srmacklem * Clear out sockets after a failure to bind one of them, so that the
823222624Srmacklem * cycle of socket creation/binding can start anew.
824222624Srmacklem */
825222624Srmacklemstatic void
826222624Srmacklemclearout_service(void)
827222624Srmacklem{
828222624Srmacklem	int i;
829222624Srmacklem
830222624Srmacklem	for (i = 0; i < sock_fdcnt; i++) {
831222624Srmacklem		if (sock_fd[i] >= 0) {
832222624Srmacklem			shutdown(sock_fd[i], SHUT_RDWR);
833222624Srmacklem			close(sock_fd[i]);
834222624Srmacklem		}
835222624Srmacklem	}
836222624Srmacklem}
837222624Srmacklem
838173281Smatteovoid
83986319Salfredsigalarm_handler(void)
84074462Salfred{
84186319Salfred
84274462Salfred	grace_expired = 1;
84374462Salfred}
84474462Salfred
84574462Salfredvoid
84630376Scharnierusage()
84730376Scharnier{
848177666Sdfr	errx(1, "usage: rpc.lockd [-d <debuglevel>]"
849355368Srpokala	    " [-F] [-g <grace period>] [-h <bindip>] [-p <port>]");
85030376Scharnier}
85175631Salfred
85275631Salfred/*
85375631Salfred * init_nsm --
85475631Salfred *	Reset the NSM state-of-the-world and acquire its state.
85575631Salfred */
85675631Salfredvoid
85775631Salfredinit_nsm(void)
85875631Salfred{
85975631Salfred	enum clnt_stat ret;
86075631Salfred	my_id id;
86175631Salfred	sm_stat stat;
86292978Salfred	char name[] = "NFS NLM";
86375631Salfred
86475631Salfred	/*
86575631Salfred	 * !!!
86675631Salfred	 * The my_id structure isn't used by the SM_UNMON_ALL call, as far
86775631Salfred	 * as I know.  Leave it empty for now.
86875631Salfred	 */
86975631Salfred	memset(&id, 0, sizeof(id));
87092978Salfred	id.my_name = name;
87175631Salfred
87275631Salfred	/*
87375631Salfred	 * !!!
87475631Salfred	 * The statd program must already be registered when lockd runs.
87575631Salfred	 */
87675631Salfred	do {
87775631Salfred		ret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON_ALL,
878121558Speter		    (xdrproc_t)xdr_my_id, &id, (xdrproc_t)xdr_sm_stat, &stat);
87975631Salfred		if (ret == RPC_PROGUNAVAIL) {
880113971Sghelmer			syslog(LOG_WARNING, "%lu %s", SM_PROG,
881113971Sghelmer			    clnt_sperrno(ret));
88275631Salfred			sleep(2);
88375631Salfred			continue;
88475631Salfred		}
88575631Salfred		break;
88675631Salfred	} while (0);
88775631Salfred
88875631Salfred	if (ret != 0) {
889113971Sghelmer		syslog(LOG_ERR, "%lu %s", SM_PROG, clnt_sperrno(ret));
890113971Sghelmer		exit(1);
89175631Salfred	}
89275631Salfred
89375631Salfred	nsm_state = stat.state;
89475631Salfred
89575631Salfred	/* setup constant data for SM_MON calls */
89692978Salfred	mon_host.mon_id.my_id.my_name = localhost;
89775631Salfred	mon_host.mon_id.my_id.my_prog = NLM_PROG;
89875631Salfred	mon_host.mon_id.my_id.my_vers = NLM_SM;
89975631Salfred	mon_host.mon_id.my_id.my_proc = NLM_SM_NOTIFY;  /* bsdi addition */
90075631Salfred}
901173281Smatteo
902173281Smatteo/*
903173281Smatteo * Out of memory, fatal
904173281Smatteo */
905173281Smatteovoid out_of_mem()
906173281Smatteo{
907173281Smatteo	syslog(LOG_ERR, "out of memory");
908173281Smatteo	exit(2);
909173281Smatteo}
910