1/*
2 * Copyright (c) 2002-2010 Apple Inc.  All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * Copyright (c) 1995
25 *	A.R. Gordon (andrew.gordon@net-tel.co.uk).  All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 *    notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 *    notice, this list of conditions and the following disclaimer in the
34 *    documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 *    must display the following acknowledgement:
37 *	This product includes software developed for the FreeBSD project
38 * 4. Neither the name of the author nor the names of any co-contributors
39 *    may be used to endorse or promote products derived from this software
40 *    without specific prior written permission.
41 *
42 * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 *
54 */
55
56#ifndef lint
57static const char rcsid[] = "$FreeBSD$";
58#endif				/* not lint */
59
60/* main() function for status monitor daemon.  Some of the code in this	 */
61/* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x	 */
62/* The actual program logic is in the file procs.c			 */
63
64#include <err.h>
65#include <errno.h>
66#include <stdio.h>
67#include <stdlib.h>
68#include <oncrpc/rpc.h>
69#include <oncrpc/rpcb.h>
70#include <string.h>
71#include <ctype.h>
72#include <syslog.h>
73#include <sys/types.h>
74#include <sys/wait.h>
75#include <signal.h>
76#include <unistd.h>
77#include <fcntl.h>
78#include <sys/stat.h>
79#include <sys/sysctl.h>
80#include <sys/param.h>
81#include <libutil.h>
82#include <util.h>
83#include <spawn.h>
84#include <launch.h>
85#include "statd.h"
86
87int bindresvport_sa(int sd, struct sockaddr * sa);
88
89int statd_server = 0;		/* are we the statd server (not notify, list...) */
90int notify_only = 0;		/* just send SM_NOTIFY messages */
91int list_only = 0;		/* just list status database entries */
92
93int udpport, tcpport;
94int udp6port, tcp6port;
95
96int statudpsock, stattcpsock;
97int statudp6sock, stattcp6sock;
98
99struct pidfh *pfh = NULL;
100
101const struct nfs_conf_statd config_defaults =
102{
103	0,			/* port */
104	0,			/* send_using_tcp */
105	0,			/* simu_crash_allowed */
106	1,			/* tcp */
107	1,			/* udp */
108	0,			/* verbose */
109};
110struct nfs_conf_statd config;
111
112int log_to_stderr = 0;
113
114extern void sm_prog_1(struct svc_req * rqstp, SVCXPRT * transp);
115static void handle_sigchld(int sig);
116static void cleanup(int sig);
117static void usage(void);
118static int config_read(struct nfs_conf_statd * conf);
119
120int
121main(int argc, char **argv)
122{
123	SVCXPRT *transp;
124	struct sigaction sa;
125	int c, on = 1;
126	pid_t pid;
127	struct sockaddr_storage saddr;
128	struct sockaddr_in *sin = (struct sockaddr_in*)&saddr;
129	struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&saddr;
130	socklen_t socklen;
131	char *unnotify_host = NULL;	/* host to "unnotify" */
132	int rv, need_notify;
133
134	config = config_defaults;
135	config_read(&config);
136
137	while ((c = getopt(argc, argv, "dnlLN:")) != EOF)
138		switch (c) {
139		case 'd':
140			config.verbose = INT_MAX;
141			break;
142		case 'n':
143			if (list_only || unnotify_host)
144				usage();
145			notify_only = 1;
146			break;
147		case 'l':
148			if (notify_only || unnotify_host || (list_only == LIST_MODE_WATCH))
149				usage();
150			list_only = LIST_MODE_ONCE;
151			break;
152		case 'L':
153			if (notify_only || unnotify_host || (list_only == LIST_MODE_ONCE))
154				usage();
155			list_only = LIST_MODE_WATCH;
156			break;
157		case 'N':
158			if (notify_only || unnotify_host || list_only)
159				usage();
160			unnotify_host = optarg;
161			break;
162		default:
163			usage();
164		}
165
166	if (list_only || unnotify_host)
167		log_to_stderr = 1;
168
169	if (list_only)
170		exit(list_hosts(list_only));
171
172	if (getuid()) {
173		log(LOG_ERR, "Sorry, rpc.statd must be run as root");
174		exit(0);
175	}
176	if (unnotify_host)
177		exit(do_unnotify_host(unnotify_host));
178
179	/* Install signal handler to do cleanup */
180	signal(SIGINT, cleanup);
181	signal(SIGTERM, cleanup);
182	signal(SIGHUP, cleanup);
183	signal(SIGQUIT, cleanup);
184
185	openlog("rpc.statd", LOG_PID | LOG_CONS, LOG_DAEMON);
186	setlogmask(LOG_UPTO(LOG_LEVEL));
187
188	if (notify_only) {
189		rv = notify_hosts();
190		if (rv)
191			log(LOG_NOTICE, "statd.notify exiting %d", rv);
192		exit(rv);
193	}
194	statd_server = 1;
195	log(LOG_INFO, "statd starting");
196
197	/* claim PID file */
198	pfh = pidfile_open(_PATH_STATD_PID, 0644, &pid);
199	if (pfh == NULL) {
200		log(LOG_ERR, "can't open statd pidfile: %s (%d)", strerror(errno), errno);
201		if (errno == EEXIST) {
202			log(LOG_ERR, "statd already running, pid: %d", pid);
203			exit(0);
204		}
205		exit(2);
206	}
207	if (pidfile_write(pfh) == -1)
208		log(LOG_WARNING, "can't write to statd pidfile: %s (%d)", strerror(errno), errno);
209
210	need_notify = init_file(_PATH_STATD_DATABASE);
211	if (need_notify && !get_statd_notify_pid()) {
212		/*
213	         * It looks like there are notifications that need to be made, but that the
214	         * statd.notify service isn't running.  Let's try to start it up.
215	         */
216		log(LOG_INFO, "need to start statd notify");
217		if (statd_notify_is_loaded())
218			statd_notify_start();
219		else
220			statd_notify_load();
221	}
222
223	statudpsock = stattcpsock = -1;
224	statudp6sock = stattcp6sock = -1;
225
226	rpcb_unset(NULL, SM_PROG, SM_VERS);
227
228	if (config.udp) {
229
230		/* IPv4 */
231		if ((statudpsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
232			log(LOG_ERR, "can't create UDP IPv4 socket: %s (%d)", strerror(errno), errno);
233		if (statudpsock >= 0) {
234			sin->sin_family = AF_INET;
235			sin->sin_addr.s_addr = INADDR_ANY;
236			sin->sin_port = htons(config.port);
237			sin->sin_len = sizeof(*sin);
238			if (bindresvport_sa(statudpsock, (struct sockaddr *)sin) < 0) {
239				/* socket may still be lingering from previous incarnation */
240				/* wait a few seconds and try again */
241				sleep(6);
242				if (bindresvport_sa(statudpsock, (struct sockaddr *)sin) < 0) {
243					log(LOG_ERR, "can't bind UDP IPv4 addr: %s (%d)", strerror(errno), errno);
244					close(statudpsock);
245					statudpsock = -1;
246				}
247			}
248		}
249		if (statudpsock >= 0) {
250			socklen = sizeof(*sin);
251			if (getsockname(statudpsock, (struct sockaddr *)sin, &socklen)) {
252				log(LOG_ERR, "can't getsockname on UDP IPv4 socket: %s (%d)", strerror(errno), errno);
253				close(statudpsock);
254				statudpsock = -1;
255			} else {
256				udpport = ntohs(sin->sin_port);
257			}
258		}
259		if ((statudpsock >= 0) && ((transp = svcudp_create(statudpsock)) == NULL)) {
260			log(LOG_WARNING, "cannot create UDP IPv4 service");
261			close(statudpsock);
262			statudpsock = -1;
263		}
264		if ((statudpsock >= 0) && !svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_UDP)) {
265			log(LOG_WARNING, "unable to register IPv4 (SM_PROG, SM_VERS, UDP)");
266			svc_destroy(transp);
267			close(statudpsock);
268			statudpsock = -1;
269		}
270
271		/* IPv6 */
272		if ((statudp6sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
273			log(LOG_ERR, "can't create UDP IPv6 socket: %s (%d)", strerror(errno), errno);
274		if (statudp6sock >= 0) {
275			setsockopt(statudp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
276			sin6->sin6_family = AF_INET6;
277			sin6->sin6_addr = in6addr_any;
278			sin6->sin6_port = htons(config.port);
279			sin6->sin6_len = sizeof(*sin6);
280			if (bindresvport_sa(statudp6sock, (struct sockaddr *)sin6) < 0) {
281				/* socket may still be lingering from previous incarnation */
282				/* wait a few seconds and try again */
283				sleep(6);
284				if (bindresvport_sa(statudp6sock, (struct sockaddr *)sin6) < 0) {
285					log(LOG_ERR, "can't bind UDP IPv6 addr: %s (%d)", strerror(errno), errno);
286					close(statudp6sock);
287					statudp6sock = -1;
288				}
289			}
290		}
291		if (statudp6sock >= 0) {
292			socklen = sizeof(*sin6);
293			if (getsockname(statudp6sock, (struct sockaddr *)sin6, &socklen)) {
294				log(LOG_ERR, "can't getsockname on UDP IPv6 socket: %s (%d)", strerror(errno), errno);
295				close(statudp6sock);
296				statudp6sock = -1;
297			} else {
298				udp6port = ntohs(sin6->sin6_port);
299			}
300		}
301		if ((statudp6sock >= 0) && ((transp = svcudp_create(statudp6sock)) == NULL)) {
302			log(LOG_WARNING, "cannot create UDP IPv6 service");
303			close(statudp6sock);
304			statudp6sock = -1;
305		}
306		if ((statudp6sock >= 0) && !svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_UDP)) {
307			log(LOG_WARNING, "unable to register IPv6 (SM_PROG, SM_VERS, UDP)");
308			svc_destroy(transp);
309			close(statudp6sock);
310			statudp6sock = -1;
311		}
312
313	}
314
315	if (config.tcp) {
316
317		/* IPv4 */
318		if ((stattcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
319			log(LOG_ERR, "can't create TCP IPv4 socket: %s (%d)", strerror(errno), errno);
320		if (stattcpsock >= 0) {
321			if (setsockopt(stattcpsock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
322				log(LOG_WARNING, "setsockopt TCP IPv4 SO_REUSEADDR: %s (%d)", strerror(errno), errno);
323			sin->sin_family = AF_INET;
324			sin->sin_addr.s_addr = INADDR_ANY;
325			sin->sin_port = htons(config.port);
326			sin->sin_len = sizeof(*sin);
327			if (bindresvport_sa(stattcpsock, (struct sockaddr *)sin) < 0) {
328				log(LOG_ERR, "can't bind TCP IPv4 addr: %s (%d)", strerror(errno), errno);
329				close(stattcpsock);
330				stattcpsock = -1;
331			}
332		}
333		if (stattcpsock >= 0) {
334			socklen = sizeof(*sin);
335			if (getsockname(stattcpsock, (struct sockaddr *)sin, &socklen)) {
336				log(LOG_ERR, "can't getsockname on TCP IPv4 socket: %s (%d)", strerror(errno), errno);
337				close(stattcpsock);
338				stattcpsock = -1;
339			} else {
340				tcpport = ntohs(sin->sin_port);
341			}
342		}
343		if ((stattcpsock >= 0) && ((transp = svctcp_create(stattcpsock, 0, 0)) == NULL)) {
344			log(LOG_WARNING, "cannot create TCP IPv4 service");
345			close(stattcpsock);
346			stattcpsock = -1;
347		}
348		if ((stattcpsock >= 0) && !svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_TCP)) {
349			log(LOG_WARNING, "unable to register IPv4 (SM_PROG, SM_VERS, TCP)");
350			svc_destroy(transp);
351			close(stattcpsock);
352			stattcpsock = -1;
353		}
354
355		/* IPv6 */
356		if ((stattcp6sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
357			log(LOG_ERR, "can't create TCP IPv6 socket: %s (%d)", strerror(errno), errno);
358		if (stattcp6sock >= 0) {
359			if (setsockopt(stattcp6sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
360				log(LOG_WARNING, "setsockopt TCP IPv6 SO_REUSEADDR: %s (%d)", strerror(errno), errno);
361			setsockopt(stattcp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
362			sin6->sin6_family = AF_INET6;
363			sin6->sin6_addr = in6addr_any;
364			sin6->sin6_port = htons(config.port);
365			sin6->sin6_len = sizeof(*sin6);
366			if (bindresvport_sa(stattcp6sock, (struct sockaddr *)sin6) < 0) {
367				log(LOG_ERR, "can't bind TCP IPv6 addr: %s (%d)", strerror(errno), errno);
368				close(stattcp6sock);
369				stattcp6sock = -1;
370			}
371		}
372		if (stattcp6sock >= 0) {
373			socklen = sizeof(*sin6);
374			if (getsockname(stattcp6sock, (struct sockaddr *)sin6, &socklen)) {
375				log(LOG_ERR, "can't getsockname on TCP IPv6 socket: %s (%d)", strerror(errno), errno);
376				close(stattcp6sock);
377				stattcp6sock = -1;
378			} else {
379				tcp6port = ntohs(sin6->sin6_port);
380			}
381		}
382		if ((stattcp6sock >= 0) && ((transp = svctcp_create(stattcp6sock, 0, 0)) == NULL)) {
383			log(LOG_WARNING, "cannot create TCP IPv6 service");
384			close(stattcp6sock);
385			stattcp6sock = -1;
386		}
387		if ((stattcp6sock >= 0) && !svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_TCP)) {
388			log(LOG_WARNING, "unable to register IPv6 (SM_PROG, SM_VERS, TCP)");
389			svc_destroy(transp);
390			close(stattcp6sock);
391			stattcp6sock = -1;
392		}
393
394	}
395
396	if ((statudp6sock < 0) && (stattcp6sock < 0))
397		log(LOG_WARNING, "Can't create NSM IPv6 sockets");
398	if ((statudpsock < 0) && (stattcpsock < 0))
399		log(LOG_WARNING, "Can't create NSM IPv4 sockets");
400	if ((statudp6sock < 0) && (stattcp6sock < 0) &&
401	    (statudpsock < 0) && (stattcpsock < 0)) {
402		log(LOG_ERR, "Can't create any NSM sockets!");
403		exit(1);
404	}
405
406	/* Install signal handler to collect exit status of child processes	 */
407	sa.sa_handler = handle_sigchld;
408	sigemptyset(&sa.sa_mask);
409	sigaddset(&sa.sa_mask, SIGCHLD);
410	sa.sa_flags = SA_RESTART;
411	sigaction(SIGCHLD, &sa, NULL);
412
413	svc_run();		/* Should never return */
414	exit(1);
415}
416
417static void
418usage(void)
419{
420	fprintf(stderr, "usage: rpc.statd [-d] [ -n | -l | -L | -N hostname ]\n");
421	exit(1);
422}
423
424/*
425 * get the PID of the running statd
426 */
427pid_t
428get_statd_pid(void)
429{
430	char pidbuf[128], *pidend;
431	int fd, len, rv;
432	pid_t pid;
433	struct flock lock;
434
435	if ((fd = open(_PATH_STATD_PID, O_RDONLY)) < 0) {
436		DEBUG(9, "%s: %s (%d)", _PATH_STATD_PID, strerror(errno), errno);
437		return (0);
438	}
439	len = sizeof(pidbuf) - 1;
440	if ((len = read(fd, pidbuf, len)) < 0) {
441		DEBUG(9, "%s: %s (%d)", _PATH_STATD_PID, strerror(errno), errno);
442		return (0);
443	}
444	/* parse PID */
445	pidbuf[len] = '\0';
446	pid = strtol(pidbuf, &pidend, 10);
447	if (!len || (pid < 1)) {
448		DEBUG(1, "%s: bogus pid: %s", _PATH_STATD_PID, pidbuf);
449		return (0);
450	}
451	/* check for lock on file by PID */
452	lock.l_type = F_RDLCK;
453	lock.l_whence = SEEK_SET;
454	lock.l_start = 0;
455	lock.l_len = 0;
456	rv = fcntl(fd, F_GETLK, &lock);
457	close(fd);
458	if (rv != 0) {
459		DEBUG(1, "%s: fcntl: %s (%d)", _PATH_STATD_PID, strerror(errno), errno);
460		return (0);
461	} else if (lock.l_type == F_UNLCK) {
462		DEBUG(8, "%s: not locked\n", _PATH_STATD_PID);
463		return (0);
464	}
465	return (pid);
466}
467
468/*
469 * get the PID of the running statd.notify
470 */
471pid_t
472get_statd_notify_pid(void)
473{
474	char pidbuf[128], *pidend;
475	int fd, len, rv;
476	pid_t pid;
477	struct flock lock;
478
479	if ((fd = open(_PATH_STATD_NOTIFY_PID, O_RDONLY)) < 0) {
480		DEBUG(9, "%s: %s (%d)", _PATH_STATD_NOTIFY_PID, strerror(errno), errno);
481		return (0);
482	}
483	len = sizeof(pidbuf) - 1;
484	if ((len = read(fd, pidbuf, len)) < 0) {
485		DEBUG(9, "%s: %s (%d)", _PATH_STATD_NOTIFY_PID, strerror(errno), errno);
486		return (0);
487	}
488	/* parse PID */
489	pidbuf[len] = '\0';
490	pid = strtol(pidbuf, &pidend, 10);
491	if (!len || (pid < 1)) {
492		DEBUG(1, "%s: bogus pid: %s", _PATH_STATD_NOTIFY_PID, pidbuf);
493		return (0);
494	}
495	/* check for lock on file by PID */
496	lock.l_type = F_RDLCK;
497	lock.l_whence = SEEK_SET;
498	lock.l_start = 0;
499	lock.l_len = 0;
500	rv = fcntl(fd, F_GETLK, &lock);
501	close(fd);
502	if (rv != 0) {
503		DEBUG(1, "%s: fcntl: %s (%d)", _PATH_STATD_NOTIFY_PID, strerror(errno), errno);
504		return (0);
505	} else if (lock.l_type == F_UNLCK) {
506		DEBUG(8, "%s: not locked\n", _PATH_STATD_NOTIFY_PID);
507		return (0);
508	}
509	return (pid);
510}
511
512
513/* handle_sigchld ---------------------------------------------------------- */
514/*
515   Purpose:	Catch SIGCHLD and collect process status
516   Returns:	Nothing.
517   Notes:	No special action required, other than to collect the
518		process status and hence allow the child to die:
519		we only use child processes for asynchronous transmission
520		of SM_NOTIFY to other systems, so it is normal for the
521		children to exit when they have done their work.
522*/
523
524static void
525handle_sigchld(int sig __unused)
526{
527	int pid, status;
528	pid = wait4(-1, &status, WNOHANG, (struct rusage *) 0);
529	if (!pid)
530		log(LOG_ERR, "Phantom SIGCHLD??");
531	else if (status == 0)
532		DEBUG(2, "Child %d exited OK", pid);
533	else
534		log(LOG_ERR, "Child %d failed with status %d", pid, WEXITSTATUS(status));
535}
536
537/* cleanup ------------------------------------------------------ */
538/*
539   Purpose:	call pid file cleanup function on signal
540   Returns:	Nothing
541*/
542
543static void
544cleanup(int sig)
545{
546	if (statd_server) {
547		/* update state to "down" on our way out */
548		status_info->fh_state = htonl(ntohl(status_info->fh_state) + 1);
549		sync_file();
550		/* unregister statd service */
551		alarm(1); /* XXX 5028243 in case rpcb_unset() gets hung up during shutdown */
552		rpcb_unset(NULL, SM_PROG, SM_VERS);
553	}
554	pidfile_remove(pfh);
555	exit((sig == SIGTERM) ? 0 : 1);
556}
557
558
559/*
560 * read the statd values from nfs.conf
561 */
562static int
563config_read(struct nfs_conf_statd * conf)
564{
565	FILE *f;
566	size_t len, linenum = 0;
567	char *line, *p, *key, *value;
568	long val;
569
570	if (!(f = fopen(_PATH_NFS_CONF, "r"))) {
571		if (errno != ENOENT)
572			log(LOG_WARNING, "%s", _PATH_NFS_CONF);
573		return (1);
574	}
575	for (; (line = fparseln(f, &len, &linenum, NULL, 0)); free(line)) {
576		if (len <= 0)
577			continue;
578		/* trim trailing whitespace */
579		p = line + len - 1;
580		while ((p > line) && isspace(*p))
581			*p-- = '\0';
582		/* find key start */
583		key = line;
584		while (isspace(*key))
585			key++;
586		/* find equals/value */
587		value = p = strchr(line, '=');
588		if (p)		/* trim trailing whitespace on key */
589			do { *p-- = '\0'; } while ((p > line) && isspace(*p));
590		/* find value start */
591		if (value)
592			do { value++; } while (isspace(*value));
593
594		/* all statd keys start with "nfs.statd." */
595		if (strncmp(key, "nfs.statd.", 10)) {
596			DEBUG(4, "%4ld %s=%s\n", linenum, key, value ? value : "");
597			continue;
598		}
599		val = !value ? 1 : strtol(value, NULL, 0);
600		DEBUG(1, "%4ld %s=%s (%d)\n", linenum, key, value ? value : "", val);
601
602		if (!strcmp(key, "nfs.statd.port")) {
603			if (value && val)
604				conf->port = val;
605		} else if (!strcmp(key, "nfs.statd.send_using_tcp")) {
606			conf->send_using_tcp = val;
607		} else if (!strcmp(key, "nfs.statd.simu_crash_allowed")) {
608			conf->simu_crash_allowed = val;
609		} else if (!strcmp(key, "nfs.statd.tcp")) {
610			conf->tcp = val;
611		} else if (!strcmp(key, "nfs.statd.udp")) {
612			conf->udp = val;
613		} else if (!strcmp(key, "nfs.statd.verbose")) {
614			conf->verbose = val;
615		} else {
616			DEBUG(2, "ignoring unknown config value: %4ld %s=%s\n", linenum, key, value ? value : "");
617		}
618
619	}
620
621	fclose(f);
622	return (0);
623}
624
625/*
626 * run an external program
627 */
628static int
629safe_exec(char *const argv[], int silent)
630{
631	posix_spawn_file_actions_t psfileact, *psfileactp = NULL;
632	pid_t pid;
633	int status;
634	extern char **environ;
635
636	if (silent) {
637		psfileactp = &psfileact;
638		if ((status = posix_spawn_file_actions_init(psfileactp))) {
639			log(LOG_ERR, "spawn init of %s failed: %s (%d)", argv[0], strerror(status), status);
640			return (1);
641		}
642		posix_spawn_file_actions_addopen(psfileactp, STDIN_FILENO, "/dev/null", O_RDONLY, 0);
643		posix_spawn_file_actions_addopen(psfileactp, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
644		posix_spawn_file_actions_addopen(psfileactp, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
645	}
646	status = posix_spawn(&pid, argv[0], psfileactp, NULL, argv, environ);
647	if (psfileactp)
648		posix_spawn_file_actions_destroy(psfileactp);
649	if (status) {
650		log(LOG_ERR, "spawn of %s failed: %s (%d)", argv[0], strerror(status), status);
651		return (1);
652	}
653	while ((waitpid(pid, &status, 0) == -1) && (errno == EINTR))
654		usleep(1000);
655	if (WIFSIGNALED(status)) {
656		log(LOG_ERR, "%s aborted by signal %d", argv[0], WTERMSIG(status));
657		return (1);
658	} else if (WIFSTOPPED(status)) {
659		log(LOG_ERR, "%s stopped by signal %d ?", argv[0], WSTOPSIG(status));
660		return (1);
661	} else if (WEXITSTATUS(status) && !silent) {
662		log(LOG_ERR, "%s exited with status %d", argv[0], WEXITSTATUS(status));
663	}
664	return (WEXITSTATUS(status));
665}
666
667int
668statd_notify_load(void)
669{
670	/*
671         * XXX Sorry, but it's just so much simpler to exec launchctl than to
672         * try to read the plist file, convert it to launchd data, and submit
673         * it ourselves.
674         */
675	const char *const args[] = {_PATH_LAUNCHCTL, "load", _PATH_STATD_NOTIFY_PLIST, NULL};
676	return safe_exec((char *const *) args, 1);
677}
678
679int
680statd_notify_is_loaded(void)
681{
682	launch_data_t msg, resp;
683	int rv = 0;
684
685	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
686	if (!msg)
687		return (0);
688	launch_data_dict_insert(msg, launch_data_new_string(_STATD_NOTIFY_SERVICE_LABEL), LAUNCH_KEY_GETJOB);
689
690	resp = launch_msg(msg);
691	if (resp) {
692		if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY)
693			rv = 1;
694		launch_data_free(resp);
695	} else {
696		log(LOG_ERR, "launch_msg(): %m");
697	}
698
699	launch_data_free(msg);
700	return (rv);
701}
702
703int
704statd_notify_start(void)
705{
706	launch_data_t msg, resp;
707	int rv = 1;
708
709	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
710	if (!msg)
711		return (1);
712	launch_data_dict_insert(msg, launch_data_new_string(_STATD_NOTIFY_SERVICE_LABEL), LAUNCH_KEY_STARTJOB);
713
714	resp = launch_msg(msg);
715	if (!resp) {
716		rv = errno;
717	} else {
718		if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)
719			rv = launch_data_get_errno(resp);
720		launch_data_free(resp);
721	}
722
723	launch_data_free(msg);
724	return (rv);
725}
726
727#pragma clang diagnostic push
728#pragma clang diagnostic ignored "-Wformat-nonliteral"
729
730/*
731 * our own little logging function...
732 */
733void
734SYSLOG(int pri, const char *fmt,...)
735{
736	va_list ap;
737
738	if (pri > LOG_LEVEL)
739		return;
740
741	va_start(ap, fmt);
742	if (log_to_stderr) {
743		vfprintf(stderr, fmt, ap);
744		fputc('\n', stderr);
745		fflush(stderr);
746	} else {
747		vsyslog(pri, fmt, ap);
748	}
749	va_end(ap);
750}
751#pragma clang diagnostic pop
752
753/*
754 * Compare the addresses in two addrinfo structures.
755 */
756static int
757addrinfo_cmp(struct addrinfo *a1, struct addrinfo *a2)
758{
759	if (a1->ai_family != a2->ai_family)
760		return (a1->ai_family - a2->ai_family);
761	if (a1->ai_addrlen != a2->ai_addrlen)
762		return (a1->ai_addrlen - a2->ai_addrlen);
763	return bcmp(a1->ai_addr, a2->ai_addr, a1->ai_addrlen);
764}
765
766/*
767 * Resolve the given host name to a list of usable addresses
768 */
769int
770getaddresslist(const char *name, struct addrinfo **ailistp)
771{
772	struct addrinfo aihints, *ailist = NULL, *ai, *aiprev, *ainext, *aidiscard;
773	char namebuf[NI_MAXHOST];
774	const char *hostname;
775
776	hostname = name;
777	if ((hostname[0] == '[') && (hostname[strlen(hostname)-1] == ']')) {
778		/* Looks like an IPv6 literal in brackets */
779		strlcpy(namebuf, hostname+1, sizeof(namebuf));
780		namebuf[strlen(namebuf)-1] = '\0';
781		hostname = namebuf;
782	}
783
784	bzero(&aihints, sizeof(aihints));
785	aihints.ai_flags = AI_ADDRCONFIG;
786	if (getaddrinfo(hostname, NULL, &aihints, &ailist))
787		return (ENOENT);
788
789	/* strip out addresses that don't match the options given */
790	aidiscard = NULL;
791	aiprev = NULL;
792
793	for (ai = ailist; ai; ai = ainext) {
794		ainext = ai->ai_next;
795
796		/* eliminate unknown protocol families */
797		if ((ai->ai_family != AF_INET) && (ai->ai_family != AF_INET6))
798			goto discard;
799
800		/* Eliminate duplicate addresses with different socktypes. */
801		if (aiprev && (ai->ai_socktype != aiprev->ai_socktype) &&
802		    !addrinfo_cmp(aiprev, ai))
803			goto discard;
804
805		aiprev = ai;
806		continue;
807discard:
808		/* Add ai to the discard list */
809		if (aiprev)
810			aiprev->ai_next = ai->ai_next;
811		else
812			ailist = ai->ai_next;
813		ai->ai_next = aidiscard;
814		aidiscard = ai;
815	}
816
817	/* free up any discarded addresses */
818	if (aidiscard)
819		freeaddrinfo(aidiscard);
820
821	*ailistp = ailist;
822	return (0);
823}
824
825