1/*
2 * Copyright (c) 1999-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) 1989, 1993, 1994
25 *	The Regents of the University of California.  All rights reserved.
26 *
27 * This code is derived from software contributed to Berkeley by
28 * Rick Macklem at The University of Guelph.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 *    notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 *    notice, this list of conditions and the following disclaimer in the
37 *    documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 *    must display the following acknowledgement:
40 *	This product includes software developed by the University of
41 *	California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 *    may be used to endorse or promote products derived from this software
44 *    without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 */
58
59#include <stdio.h>
60#include <stdlib.h>
61#include <stdarg.h>
62#include <unistd.h>
63#include <fcntl.h>
64#include <string.h>
65#include <ctype.h>
66#include <signal.h>
67#include <notify.h>
68#include <errno.h>
69#include <err.h>
70#include <pthread.h>
71#include <spawn.h>
72#include <dns_sd.h>
73
74#include <mach/mach.h>
75#include <mach/host_special_ports.h>
76
77#include <sys/syslog.h>
78#include <sys/param.h>
79#include <sys/types.h>
80#include <sys/stat.h>
81#include <sys/event.h>
82#include <sys/time.h>
83#include <sys/mount.h>
84#include <sys/sysctl.h>
85
86#include <libutil.h>
87#include <util.h>
88#include <launch.h>
89
90#include <netinet/in.h>
91#include <oncrpc/rpc.h>
92#include <oncrpc/rpcb.h>
93#include <nfs/rpcv2.h>
94#include <nfs/nfsproto.h>
95
96#include <CoreFoundation/CoreFoundation.h>
97#include <ServiceManagement/ServiceManagement.h>
98#include <ServiceManagement/ServiceManagement_Private.h>
99
100#include "lockd_mach.h"
101#include "pathnames.h"
102#include "common.h"
103
104#define GETOPT			"F:Nn:P:p:Rrtuv"
105
106#define MAX_NFSD_THREADS_SOFT	192
107#define MAX_NFSD_THREADS_HARD	512
108
109const struct nfs_conf_server config_defaults =
110{
111	0,		/* async */
112	1,		/* bonjour */
113	0,		/* bonjour_local_domain_only */
114	64,		/* export_hash_size */
115	1,		/* fsevents */
116	0,		/* mount_port */
117	0,		/* mount_regular_files */
118	1,		/* mount_require_resv_port */
119	8,		/* nfsd_threads */
120	NFS_PORT,	/* port */
121	64,		/* reqcache_size */
122	128,		/* request_queue_length */
123	0,		/* require_resv_port */
124	1,		/* tcp */
125	1,		/* udp */
126	1,		/* user_stats */
127	0,		/* verbose */
128	1000,		/* wg_delay */
129	0,		/* wg_delay_v3 */
130};
131
132/* globals */
133pthread_attr_t pattr;
134struct nfs_conf_server config;
135char exportsfilepath[MAXPATHLEN];
136volatile int gothup, gotterm;
137int checkexports = 0, log_to_stderr = 0;
138int nfsudpport = 0, nfstcpport = 0;
139int nfsudp6port = 0, nfstcp6port = 0;
140int mountudpport = 0, mounttcpport = 0;
141int mountudp6port = 0, mounttcp6port = 0;
142time_t recheckexports_until = 0;
143int recheckexports = 0;
144
145DNSServiceRef nfs_dns_service;
146
147static int config_read(struct nfs_conf_server *);
148static void config_sanity_check(struct nfs_conf_server *conf);
149static void config_sysctl_changed(struct nfs_conf_server *, struct nfs_conf_server *);
150static void config_loop(void);
151
152static pid_t get_pid(const char *);
153static pid_t get_nfsd_pid(void);
154static void signal_nfsd(int);
155static void sigmux(int);
156
157static int service_is_enabled(CFStringRef);
158static int service_is_loaded(CFStringRef);
159static int nfsd_is_enabled(void);
160static int nfsd_is_loaded(void);
161static int nfsd_is_running(void);
162
163static int nfsd_enable(void);
164static int nfsd_disable(void);
165static int nfsd_load(void);
166static int nfsd_unload(void);
167static int nfsd_start(void);
168static int nfsd_stop(void);
169
170static void register_services(void);
171static int safe_exec(char *const*, int);
172static void do_lockd_ping(void);
173static void do_lockd_shutdown(void);
174static int rquotad_start(void);
175static int rquotad_stop(void);
176
177static void
178usage(void)
179{
180	fprintf(stderr, "usage: nfsd [-NRrtuv] [-F export_file] [-n num_servers] "
181			"[-p nfsport] [-P mountport] [command]\n");
182	fprintf(stderr, "commands: enable, disable, start, stop, restart, update, status, checkexports, verbose [up|down]\n");
183	exit(1);
184}
185
186int
187main(int argc, char *argv[], __unused char *envp[])
188{
189	struct pidfh *nfsd_pfh, *mountd_pfh;
190	pid_t pid;
191	struct stat st;
192	int ch, reregister, rv;
193	int tcpflag, udpflag, protocnt;
194	int nfsdcnt, nfsport, mountport;
195	int mount_require_resv_port = 1;
196	int mount_regular_files = 0;
197	extern int optind;
198
199	/* set defaults then do config_read() to get config values */
200	config = config_defaults;
201	config_read(&config);
202
203	/* init command-line flags */
204	reregister = 0;
205	nfsdcnt = 0;
206	protocnt = tcpflag = udpflag = 0;
207	nfsport = mountport = 0;
208	exportsfilepath[0] = '\0';
209
210	while ((ch = getopt(argc, argv, GETOPT)) != EOF)
211		switch (ch) {
212		// nfsd
213		case 'n':
214			nfsdcnt = atoi(optarg);
215			break;
216		case 'p':
217			nfsport = atoi(optarg);
218			break;
219		case 'r':
220			reregister = 1;
221			break;
222		case 't':
223			tcpflag = 1;
224			protocnt++;
225			break;
226		case 'u':
227			udpflag = 1;
228			protocnt++;
229			break;
230		// mountd
231		case 'F':
232			strlcpy(exportsfilepath, optarg, MAXPATHLEN);
233			break;
234		case 'N':
235			mount_require_resv_port = 0;
236			break;
237		case 'P':
238			mountport = atoi(optarg);
239			break;
240		case 'R':
241			mount_regular_files = 1;
242			break;
243		// miscellaneous
244		case 'v':
245			config.verbose++;
246			break;
247		default:
248		case '?':
249			usage();
250		};
251	argv += optind;
252	argc -= optind;
253
254	/* set config values for flags specified */
255	if (nfsdcnt)
256		config.nfsd_threads = nfsdcnt;
257	if (protocnt) {
258		config.tcp = tcpflag;
259		config.udp = udpflag;
260	}
261	if (nfsport)
262		config.port = nfsport;
263	if (mountport)
264		config.mount_port = mountport;
265	if (!mount_require_resv_port)
266		config.mount_require_resv_port = mount_require_resv_port;
267	if (mount_regular_files)
268		config.mount_regular_files = mount_regular_files;
269	if (!exportsfilepath[0])
270		strlcpy(exportsfilepath, _PATH_EXPORTS, sizeof(exportsfilepath));
271
272	if (reregister || (argc > 0))
273		log_to_stderr = 1;
274
275	if (reregister) {
276		signal_nfsd(SIGHUP);
277		exit(0);
278	}
279
280	rv = 0;
281
282	if (argc > 0) {
283		/* process the given, unprivileged command */
284		if (!strcmp(argv[0], "status")) {
285			int enabled, loaded;
286			enabled = nfsd_is_enabled();
287			printf("nfsd service is %s\n", enabled ? "enabled" : "disabled");
288			if (config.verbose) {
289				loaded = nfsd_is_loaded();
290				printf("nfsd service is %s\n", loaded ? "loaded" : "not loaded");
291			}
292			pid = get_nfsd_pid();
293			if (pid <= 0) {
294				printf("nfsd is not running\n");
295			} else {
296				int cur = 0;
297				sysctl_get("vfs.generic.nfs.server.nfsd_thread_count", &cur);
298				printf("nfsd is running (pid %d, %d threads)\n", pid, cur);
299			}
300			rv = enabled ? 0 : 1;
301			if (config.verbose) {
302				/* print info about related daemons too */
303				/* lockd */
304				enabled = service_is_enabled(CFSTR(_LOCKD_SERVICE_LABEL));
305				printf("lockd service is %s\n", enabled ? "enabled" : "disabled");
306				loaded = service_is_loaded(CFSTR(_LOCKD_SERVICE_LABEL));
307				printf("lockd service is %s\n", loaded ? "loaded" : "not loaded");
308				pid = get_pid(_PATH_LOCKD_PID);
309				if (pid <= 0)
310					printf("lockd is not running\n");
311				else
312					printf("lockd is running (pid %d)\n", pid);
313				/* statd.notify */
314				enabled = service_is_enabled(CFSTR(_STATD_NOTIFY_SERVICE_LABEL));
315				printf("statd.notify service is %s\n", enabled ? "enabled" : "disabled");
316				loaded = service_is_loaded(CFSTR(_STATD_NOTIFY_SERVICE_LABEL));
317				printf("statd.notify service is %s\n", loaded ? "loaded" : "not loaded");
318				pid = get_pid(_PATH_STATD_NOTIFY_PID);
319				if (pid <= 0)
320					printf("statd.notify is not running\n");
321				else
322					printf("statd.notify is running (pid %d)\n", pid);
323				/* statd */
324				loaded = service_is_loaded(CFSTR(_STATD_SERVICE_LABEL));
325				printf("statd service is %s\n", loaded ? "loaded" : "not loaded");
326				pid = get_pid(_PATH_STATD_PID);
327				if (pid <= 0)
328					printf("statd is not running\n");
329				else
330					printf("statd is running (pid %d)\n", pid);
331				/* rquotad */
332				loaded = service_is_loaded(CFSTR(_RQUOTAD_SERVICE_LABEL));
333				printf("rquotad service is %s\n", loaded ? "loaded" : "not loaded");
334				pid = get_pid(_PATH_RQUOTAD_PID);
335				if (pid <= 0)
336					printf("rquotad is not running\n");
337				else
338					printf("rquotad is running (pid %d)\n", pid);
339			}
340			exit(rv);
341		} else if (!strcmp(argv[0], "checkexports")) {
342			checkexports = 1;
343			mountd_init();
344			rv = get_exportlist();
345			exit(rv);
346		}
347	}
348
349	if (getuid()) {
350		printf("Sorry, nfsd must be run as root\n");
351		printf("unprivileged usage: nfsd [ status | [-F file] checkexports]\n");
352		/* try to make sure the nfsd service isn't loaded in the per-user launchd */
353		if (nfsd_is_loaded())
354			nfsd_unload();
355		exit(2);
356	}
357
358	if (argc > 0) {
359		/* process the given, privileged command */
360		if (!strcmp(argv[0], "enable")) {
361			if (!nfsd_is_enabled()) {
362				rv = nfsd_enable();
363			} else {
364				printf("The nfsd service is already enabled.\n");
365				/* make sure it's running */
366				if (!nfsd_is_running())
367					rv = nfsd_is_loaded() ? nfsd_start() : nfsd_load();
368			}
369		} else if (!strcmp(argv[0], "disable")) {
370			if (nfsd_is_enabled()) {
371				rv = nfsd_disable();
372			} else {
373				printf("The nfsd service is already disabled.\n");
374				if (nfsd_is_loaded())
375					rv = nfsd_unload();
376			}
377		} else if (!strcmp(argv[0], "start")) {
378			if (nfsd_is_running()) {
379				printf("The nfsd service is already running.\n");
380			} else {
381				printf("Starting the nfsd service%s\n",
382					nfsd_is_enabled() ? "" : " (use 'enable' to make permanent)");
383				rv = nfsd_is_loaded() ? nfsd_start() : nfsd_load();
384			}
385		} else if (!strcmp(argv[0], "stop")) {
386			if (!nfsd_is_running()) {
387				printf("The nfsd service is not running.\n");
388			} else {
389				printf("Stopping the nfsd service%s\n",
390					!nfsd_is_enabled() ? "" : " (use 'disable' to make permanent)");
391				rv = nfsd_unload();
392			}
393		} else if (!strcmp(argv[0], "restart")) {
394			if (!nfsd_is_running() || !nfsd_is_loaded())
395				printf("The nfsd service does not appear to be running.\n");
396			if (nfsd_is_running()) {
397				/* should be immediately restarted if /etc/exports exists */
398				rv = nfsd_stop();
399			} else {
400				printf("Starting the nfsd service%s\n",
401					nfsd_is_enabled() ? "" : " (use 'enable' to permanently enable)");
402				rv = nfsd_is_loaded() ? nfsd_start() : nfsd_load();
403			}
404		} else if (!strcmp(argv[0], "update")) {
405			signal_nfsd(SIGHUP);
406		} else if (!strcmp(argv[0], "verbose")) {
407			argc--;
408			argv++;
409			for (;argc;argc--,argv++) {
410				if (!strcmp(argv[0], "up"))
411					signal_nfsd(SIGUSR1);
412				else if (!strcmp(argv[0], "down"))
413					signal_nfsd(SIGUSR2);
414				else
415					errx(1, "unknown verbose command: %s", argv[0]);
416				usleep(100000);
417			}
418		} else {
419			warnx("unknown command: %s", argv[0]);
420			usage();
421		}
422		exit(rv);
423	}
424
425	pthread_attr_init(&pattr);
426	pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
427
428	/* set up signal handling */
429	signal(SIGQUIT, SIG_IGN);
430	signal(SIGPIPE, SIG_IGN);
431	signal(SIGSYS, sigmux);
432	signal(SIGTERM, sigmux);
433	signal(SIGHUP, sigmux);
434	signal(SIGUSR1, sigmux);
435	signal(SIGUSR2, sigmux);
436
437	/* set up logging */
438	openlog(NULL, LOG_PID, LOG_DAEMON);
439	setlogmask(LOG_UPTO(LOG_LEVEL));
440
441	/* quick config sanity check */
442	config_sanity_check(&config);
443
444	/* we really shouldn't be running if there's no exports file */
445	if (stat(exportsfilepath, &st)) {
446		/* exports file doesn't exist, so just unload ourselves */
447		log(LOG_WARNING, "no exports file, unloading nfsd service");
448		rv = nfsd_unload();
449		exit(rv);
450	}
451
452	/* claim PID files */
453	nfsd_pfh = pidfile_open(_PATH_NFSD_PID, 0644, &pid);
454	if (nfsd_pfh == NULL) {
455		log(LOG_ERR, "can't open nfsd pidfile: %s (%d)", strerror(errno), errno);
456		if ((errno == EACCES) && getuid())
457			log(LOG_ERR, "nfsd is expected to be run as root, not as uid %d.", getuid());
458		else if (errno == EEXIST)
459			log(LOG_ERR, "nfsd already running, pid: %d", pid);
460		exit(2);
461	}
462	if (pidfile_write(nfsd_pfh) == -1)
463		log(LOG_WARNING, "can't write to nfsd pidfile: %s (%d)", strerror(errno), errno);
464
465	mountd_pfh = pidfile_open(_PATH_MOUNTD_PID, 0644, &pid);
466	if (mountd_pfh == NULL) {
467		log(LOG_ERR, "can't open mountd pidfile: %s (%d)", strerror(errno), errno);
468		if (errno == EEXIST)
469			log(LOG_ERR, "mountd already running, pid: %d", pid);
470		exit(2);
471	}
472	if (pidfile_write(mountd_pfh) == -1)
473		log(LOG_WARNING, "can't write to mountd pidfile: %s (%d)", strerror(errno), errno);
474
475	log(LOG_NOTICE, "nfsd starting");
476	if (config.verbose)
477		log(LOG_NOTICE, "verbose level set to %d", config.verbose);
478
479	/* set up sysctl config values */
480	config_sysctl_changed(NULL, &config);
481
482	/* initialize/start mountd */
483	mountd();
484
485	/* initialize/start nfsd */
486	nfsd();
487
488	/* make sure rpc.lockd is running */
489	do_lockd_ping();
490
491	/* make sure rpc.rquotad is running */
492	rquotad_start();
493
494	/* tell others about our services */
495	register_services();
496
497	/* main thread loops to handle config updates */
498	config_loop();
499
500	/* nfsd is exiting... */
501	sysctl_set("vfs.generic.nfs.server.nfsd_thread_max", 0);
502
503	/* tell lockd to prepare to shut down */
504	do_lockd_shutdown();
505
506	/* stop rpc.rquotad */
507	rquotad_stop();
508
509	/* clean up */
510	alarm(1); /* XXX 5028243 in case rpcb_unset() gets hung up during shutdown */
511	rpcb_unset(NULL, RPCPROG_NFS, 2);
512	rpcb_unset(NULL, RPCPROG_NFS, 3);
513	rpcb_unset(NULL, RPCPROG_MNT, 1);
514	rpcb_unset(NULL, RPCPROG_MNT, 3);
515	if (nfs_dns_service)
516		DNSServiceRefDeallocate(nfs_dns_service);
517
518	/* and get out */
519	pidfile_remove(mountd_pfh);
520	pidfile_remove(nfsd_pfh);
521	exit(0);
522}
523
524/*
525 * read the NFS server values from nfs.conf
526 */
527static int
528config_read(struct nfs_conf_server *conf)
529{
530	FILE *f;
531	size_t len, linenum = 0;
532	char *line, *p, *key, *value;
533	long val;
534
535	if (!(f = fopen(_PATH_NFS_CONF, "r"))) {
536		if (errno != ENOENT)
537			log(LOG_WARNING, "%s", _PATH_NFS_CONF);
538		return (1);
539	}
540
541	for (;(line = fparseln(f, &len, &linenum, NULL, 0)); free(line)) {
542		if (len <= 0)
543			continue;
544		/* trim trailing whitespace */
545		p = line + len - 1;
546		while ((p > line) && isspace(*p))
547			*p-- = '\0';
548		/* find key start */
549		key = line;
550		while (isspace(*key))
551			key++;
552		/* find equals/value */
553		value = p = strchr(line, '=');
554		if (p) /* trim trailing whitespace on key */
555			do { *p-- = '\0'; } while ((p > line) && isspace(*p));
556		/* find value start */
557		if (value)
558			do { value++; } while (isspace(*value));
559
560		/* all server keys start with "nfs.server." */
561		if (strncmp(key, "nfs.server.", 11)) {
562			DEBUG(3, "%4ld %s=%s", linenum, key, value ? value : "");
563			continue;
564		}
565
566		val = !value ? 1 : strtol(value, NULL, 0);
567		DEBUG(2, "%4ld %s=%s (%d)", linenum, key, value ? value : "", val);
568
569		if (!strcmp(key, "nfs.server.async")) {
570			conf->async = val;
571		} else if (!strcmp(key, "nfs.server.bonjour")) {
572			conf->bonjour = val;
573		} else if (!strcmp(key, "nfs.server.bonjour.local_domain_only")) {
574			conf->bonjour_local_domain_only = val;
575		} else if (!strcmp(key, "nfs.server.export_hash_size")) {
576			if (value && val)
577				conf->export_hash_size = val;
578		} else if (!strcmp(key, "nfs.server.fsevents")) {
579			conf->fsevents = val;
580		} else if (!strcmp(key, "nfs.server.mount.port")) {
581			if (value && val)
582				conf->mount_port = val;
583		} else if (!strcmp(key, "nfs.server.mount.regular_files")) {
584			conf->mount_regular_files = val;
585		} else if (!strcmp(key, "nfs.server.mount.require_resv_port")) {
586			conf->mount_require_resv_port = val;
587		} else if (!strcmp(key, "nfs.server.nfsd_threads")) {
588			if (value && val)
589				conf->nfsd_threads = val;
590		} else if (!strcmp(key, "nfs.server.port")) {
591			if (value && val)
592				conf->port = val;
593		} else if (!strcmp(key, "nfs.server.reqcache_size")) {
594			if (value && val)
595				conf->reqcache_size = val;
596		} else if (!strcmp(key, "nfs.server.request_queue_length")) {
597			if (value && val)
598				conf->request_queue_length = val;
599		} else if (!strcmp(key, "nfs.server.require_resv_port")) {
600			conf->require_resv_port = val;
601		} else if (!strcmp(key, "nfs.server.tcp")) {
602			conf->tcp = val;
603		} else if (!strcmp(key, "nfs.server.udp")) {
604			conf->udp = val;
605		} else if (!strcmp(key, "nfs.server.user_stats")) {
606			conf->user_stats = val;
607		} else if (!strcmp(key, "nfs.server.verbose")) {
608			conf->verbose = val;
609		} else if (!strcmp(key, "nfs.server.wg_delay")) {
610			if (value && val)
611				conf->wg_delay = val;
612		} else if (!strcmp(key, "nfs.server.wg_delay_v3")) {
613			if (value && val)
614				conf->wg_delay_v3 = val;
615		} else {
616			DEBUG(1, "ignoring unknown config value: %4ld %s=%s", linenum, key, value ? value : "");
617		}
618
619	}
620
621	fclose(f);
622	return (0);
623}
624
625/*
626 * sanity check config values
627 */
628static void
629config_sanity_check(struct nfs_conf_server *conf)
630{
631	if (conf->nfsd_threads < 1) {
632		log(LOG_WARNING, "nfsd thread count %d; reset to %d", conf->nfsd_threads, config_defaults.nfsd_threads);
633		conf->nfsd_threads = config_defaults.nfsd_threads;
634	} else if (conf->nfsd_threads > MAX_NFSD_THREADS_SOFT) {
635		if (conf->nfsd_threads > MAX_NFSD_THREADS_HARD) {
636			log(LOG_WARNING, "nfsd thread count %d; limited to %d", conf->nfsd_threads, MAX_NFSD_THREADS_HARD);
637			conf->nfsd_threads = MAX_NFSD_THREADS_HARD;
638		}
639		log(LOG_WARNING, "Recomended maximum is %d threads. An nfsd thread count of %d may cause system performance problems.",
640		    MAX_NFSD_THREADS_SOFT, conf->nfsd_threads);
641	}
642
643	if (!config.udp && !config.tcp)
644		log(LOG_WARNING, "No network transport(s) configured.");
645}
646
647/*
648 * config_loop()
649 *
650 * Loop until terminated.
651 * Just wait for a signal or mount notification.
652 */
653static void
654config_loop(void)
655{
656	int kq, rv, gotmount = 0, exports_changed;
657	struct kevent ke;
658	struct nfs_conf_server newconf;
659	struct stat st, stnew;
660	struct timespec ts = { 10, 0 };
661
662	/* set up mount/unmount kqueue */
663	if ((kq = kqueue()) < 0) {
664		log(LOG_ERR, "kqueue: %s (%d)", strerror(errno), errno);
665		exit(1);
666	}
667	EV_SET(&ke, 0, EVFILT_FS, EV_ADD, 0, 0, 0);
668	rv = kevent(kq, &ke, 1, NULL, 0, NULL);
669	if (rv < 0) {
670		log(LOG_ERR, "kevent(EVFILT_FS): %s (%d)", strerror(errno), errno);
671		exit(1);
672	}
673
674	/* get baseline stat values for exports file */
675	stat(exportsfilepath, &st);
676
677	while (!gotterm) {
678
679		DEBUG(1, "config_loop: waiting...");
680		rv = kevent(kq, NULL, 0, &ke, 1, (recheckexports ? &ts : NULL));
681		if ((rv > 0) && !(ke.flags & EV_ERROR) && (ke.fflags & (VQ_MOUNT|VQ_UNMOUNT))) {
682			log(LOG_INFO, "mount list changed: 0x%x", ke.fflags);
683			gotmount = check_for_mount_changes();
684		}
685		if (recheckexports)  {	/* make sure we check the exports again */
686			if (!gotmount)
687				log(LOG_INFO, "rechecking exports");
688			gotmount = 1;
689		}
690
691		while (!gotterm && (gothup || gotmount)) {
692			if (gothup) {
693				DEBUG(1, "handling HUP");
694				newconf = config_defaults;
695				if (!config_read(&newconf)) {
696					config_sanity_check(&newconf);
697					/* if port/transport/reqcachesize change detected exit to initiate a restart */
698					if ((newconf.port != config.port) || (newconf.mount_port != config.mount_port) ||
699					    (newconf.tcp != config.tcp) || (newconf.udp != config.udp) ||
700					    (newconf.reqcache_size != config.reqcache_size) ||
701					    (newconf.export_hash_size != config.export_hash_size)) {
702						/* port, transport, and reqcache/export_hash size changes require a restart */
703						if (newconf.reqcache_size != config.reqcache_size)
704							log(LOG_NOTICE, "request cache size change (%d -> %d) requires restart",
705								config.reqcache_size, newconf.reqcache_size);
706						if (newconf.export_hash_size != config.export_hash_size)
707							log(LOG_NOTICE, "export hash size change (%d -> %d) requires restart",
708								config.export_hash_size, newconf.export_hash_size);
709						if ((newconf.port != config.port) || (newconf.mount_port != config.mount_port) ||
710						    (newconf.tcp != config.tcp) || (newconf.udp != config.udp))
711							log(LOG_NOTICE, "port/transport changes require restart");
712						gotterm = 1;
713						break;
714					}
715					config_sysctl_changed(&config, &newconf);
716					/* update nfsd_thread_max in kernel */
717					sysctl_set("vfs.generic.nfs.server.nfsd_thread_max", newconf.nfsd_threads);
718					/* launch any new nfsd threads */
719					if (newconf.nfsd_threads > config.nfsd_threads)
720						nfsd_start_server_threads(newconf.nfsd_threads - config.nfsd_threads);
721					/* make new config current */
722					config = newconf;
723				}
724				/* ping lockd, in case it needs to be restarted */
725				do_lockd_ping();
726				/* make sure rquotad is running */
727				rquotad_start();
728				/* reregister services */
729				register_services();
730			}
731
732			/* check if it looks like the exports file changed */
733			if (stat(exportsfilepath, &stnew)) {
734				exports_changed = 0;
735			} else {
736				exports_changed =
737					(stnew.st_dev != st.st_dev) || (stnew.st_ino != st.st_ino) ||
738					(stnew.st_ctimespec.tv_sec != st.st_ctimespec.tv_sec) ||
739					(stnew.st_ctimespec.tv_nsec != st.st_ctimespec.tv_nsec);
740				st = stnew;
741			}
742
743			/* clear export errors on HUP or exports file change */
744			if (exports_changed && clear_export_errors(0))
745				log(LOG_WARNING, "exports file changed: previous errors cleared");
746
747			gotmount = gothup = 0;
748			get_exportlist();
749		}
750	}
751}
752
753/*
754 * set config's sysctl values
755 */
756static void
757config_sysctl_changed(struct nfs_conf_server *old, struct nfs_conf_server *new)
758{
759	if (!old || (old->async != new->async))
760		sysctl_set("vfs.generic.nfs.server.async", new->async);
761	if (!old || (old->export_hash_size != new->export_hash_size))
762		sysctl_set("vfs.generic.nfs.server.export_hash_size", new->export_hash_size);
763	if (!old || (old->fsevents != new->fsevents))
764		sysctl_set("vfs.generic.nfs.server.fsevents", new->fsevents);
765	if (!old) /* should only be set at startup */
766		sysctl_set("vfs.generic.nfs.server.reqcache_size", new->reqcache_size);
767	if (!old || (old->request_queue_length != new->request_queue_length))
768		sysctl_set("vfs.generic.nfs.server.request_queue_length", new->request_queue_length);
769	if (!old || (old->require_resv_port != new->require_resv_port))
770		sysctl_set("vfs.generic.nfs.server.require_resv_port", new->require_resv_port);
771	if (!old || (old->user_stats != new->user_stats))
772		sysctl_set("vfs.generic.nfs.server.user_stats", new->user_stats);
773	if (!old || (old->wg_delay != new->wg_delay))
774		sysctl_set("vfs.generic.nfs.server.wg_delay", new->wg_delay);
775	if (!old || (old->wg_delay_v3 != new->wg_delay_v3))
776		sysctl_set("vfs.generic.nfs.server.wg_delay_v3", new->wg_delay_v3);
777}
778
779/*
780 * get a sysctl config value
781 */
782int
783sysctl_get(const char *name, int *val)
784{
785	size_t size = sizeof(int);
786
787	return sysctlbyname(name, val, &size, NULL, 0);
788}
789
790/*
791 * set a sysctl config value
792 */
793int
794sysctl_set(const char *name, int val)
795{
796	int rv = sysctlbyname(name, NULL, 0, &val, sizeof(val));
797	DEBUG(1, "sysctl_set: %s = %d, rv %d", name, val, rv);
798	return (rv);
799}
800
801/*
802 * all threads besides the main thread should have signals blocked.
803 */
804void
805set_thread_sigmask(void)
806{
807	sigset_t sigset;
808
809	sigemptyset(&sigset);
810	sigaddset(&sigset, SIGINT);
811	sigaddset(&sigset, SIGQUIT);
812	sigaddset(&sigset, SIGSYS);
813	sigaddset(&sigset, SIGPIPE);
814	sigaddset(&sigset, SIGTERM);
815	sigaddset(&sigset, SIGHUP);
816	sigaddset(&sigset, SIGUSR1);
817	sigaddset(&sigset, SIGUSR2);
818	sigaddset(&sigset, SIGABRT);
819	pthread_sigmask(SIG_BLOCK, &sigset, NULL);
820}
821
822/*
823 * generic, flag-setting signal handler
824 */
825static void
826sigmux(int sig)
827{
828	switch (sig) {
829	case SIGHUP:
830		gothup = 1;
831		log(LOG_NOTICE, "SIGHUP\n");
832		break;
833	case SIGSYS:
834		log(LOG_ERR, "missing system call: NFS not available.");
835		/*FALLTHRU*/
836	case SIGTERM:
837		gotterm = 1;
838		break;
839	case SIGUSR1:
840		config.verbose++;
841		setlogmask(LOG_UPTO(LOG_LEVEL));
842		log(LOG_WARNING, "verbose level is now %d\n", config.verbose);
843		break;
844	case SIGUSR2:
845		if (config.verbose > 0)
846			config.verbose--;
847		setlogmask(LOG_UPTO(LOG_LEVEL));
848		log(LOG_WARNING, "verbose level is now %d\n", config.verbose);
849		break;
850	}
851}
852
853/*
854 * XXX The following code should be removed when xnu is in sync with
855 * the build machine's /usr/include/mach/host_special_ports.h
856 */
857#ifndef HOST_LOCKD_PORT
858#define HOST_LOCKD_PORT                 (5 + HOST_MAX_SPECIAL_KERNEL_PORT)
859
860#define host_get_lockd_port(host, port)	\
861	(host_get_special_port((host), 			\
862	HOST_LOCAL_NODE, HOST_LOCKD_PORT, (port)))
863#endif
864
865/*
866 * Start up lockd, (which in turn should start up statd)
867 * We use a mach host special port to send a null mach message. If lockd
868 * is not running, launchd should have the receive right and will start
869 * lockd which will then ack our ping.
870 */
871static void
872do_lockd_ping(void)
873{
874	kern_return_t kr;
875	mach_port_t mp;
876
877	kr = host_get_lockd_port(mach_host_self(), &mp);
878	if (kr != KERN_SUCCESS || !MACH_PORT_VALID(mp)) {
879		log(LOG_ERR, "Can't get lockd mach port!");
880		return;
881	}
882	kr = lockd_ping(mp);
883	mach_port_destroy(mach_task_self(), mp);
884	if (kr != KERN_SUCCESS) {
885		log(LOG_ERR, "Lockd did not start!");
886		return;
887	}
888	return;
889}
890
891/*
892 * Inform lockd to prepare for shutting down.
893 */
894static void
895do_lockd_shutdown(void)
896{
897	kern_return_t kr;
898	mach_port_t mp;
899
900	kr = host_get_lockd_port(mach_host_self(), &mp);
901	if (kr != KERN_SUCCESS || !MACH_PORT_VALID(mp)) {
902		log(LOG_ERR, "Can't get lockd mach port!");
903		return;
904	}
905	kr = lockd_shutdown(mp);
906	mach_port_destroy(mach_task_self(), mp);
907	if (kr != KERN_SUCCESS) {
908		log(LOG_ERR, "lockd shutdown failed!");
909		return;
910	}
911	return;
912}
913
914/*
915 * register NFS and MOUNT services with portmap
916 */
917static void
918register_services(void)
919{
920	struct sockaddr_storage ss, ss6;
921	struct sockaddr *sa = (struct sockaddr*)&ss;
922	struct sockaddr *sa6 = (struct sockaddr*)&ss6;
923	struct sockaddr_in *sin = (struct sockaddr_in*)&ss;
924	struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&ss6;
925	int errcnt;
926
927	sin->sin_family = AF_INET;
928	sin->sin_addr.s_addr = INADDR_ANY;
929	sin->sin_len = sizeof(*sin);
930	sin6->sin6_family = AF_INET6;
931	sin6->sin6_addr = in6addr_any;
932	sin6->sin6_len = sizeof(*sin6);
933
934	/* nfsd */
935	rpcb_unset(NULL, RPCPROG_NFS, 2);
936	rpcb_unset(NULL, RPCPROG_NFS, 3);
937	if (config.udp) {
938		errcnt = 0;
939		if (nfsudpport) {
940			sin->sin_port = htons(nfsudpport);
941			if (!rpcb_set("udp", RPCPROG_NFS, 2, sa))
942				errcnt++;
943			if (!rpcb_set("udp", RPCPROG_NFS, 3, sa))
944				errcnt++;
945		}
946		if (nfsudp6port) {
947			sin6->sin6_port = htons(nfsudp6port);
948			if (!rpcb_set("udp6", RPCPROG_NFS, 2, sa6))
949				errcnt++;
950			if (!rpcb_set("udp6", RPCPROG_NFS, 3, sa6))
951				errcnt++;
952		}
953		if (errcnt)
954			log(LOG_ERR, "couldn't register NFS/UDP service.");
955	}
956	if (config.tcp) {
957		errcnt = 0;
958		if (nfstcpport) {
959			sin->sin_port = htons(nfstcpport);
960			if (!rpcb_set("tcp", RPCPROG_NFS, 2, sa))
961				errcnt++;
962			if (!rpcb_set("tcp", RPCPROG_NFS, 3, sa))
963				errcnt++;
964		}
965		if (nfstcp6port) {
966			sin6->sin6_port = htons(nfstcp6port);
967			if (!rpcb_set("tcp6", RPCPROG_NFS, 2, sa6))
968				errcnt++;
969			if (!rpcb_set("tcp6", RPCPROG_NFS, 3, sa6))
970				errcnt++;
971		}
972		if (errcnt)
973			log(LOG_ERR, "couldn't register NFS/TCP service.");
974	}
975
976	/* mountd */
977	rpcb_unset(NULL, RPCPROG_MNT, 1);
978	rpcb_unset(NULL, RPCPROG_MNT, 3);
979	if (config.udp) {
980		errcnt = 0;
981		if (mountudpport) {
982			sin->sin_port = htons(mountudpport);
983			if (!rpcb_set("udp", RPCPROG_MNT, 1, sa))
984				errcnt++;
985			if (!rpcb_set("udp", RPCPROG_MNT, 3, sa))
986				errcnt++;
987		}
988		if (mountudp6port) {
989			sin6->sin6_port = htons(mountudp6port);
990			if (!rpcb_set("udp6", RPCPROG_MNT, 1, sa6))
991				errcnt++;
992			if (!rpcb_set("udp6", RPCPROG_MNT, 3, sa6))
993				errcnt++;
994		}
995		if (errcnt)
996			log(LOG_ERR, "couldn't register MOUNT/UDP service.");
997	}
998	if (config.tcp) {
999		errcnt = 0;
1000		if (mounttcpport) {
1001			sin->sin_port = htons(mounttcpport);
1002			if (!rpcb_set("tcp", RPCPROG_MNT, 1, sa))
1003				errcnt++;
1004			if (!rpcb_set("tcp", RPCPROG_MNT, 3, sa))
1005				errcnt++;
1006		}
1007		if (mounttcp6port) {
1008			sin6->sin6_port = htons(mounttcp6port);
1009			if (!rpcb_set("tcp6", RPCPROG_MNT, 1, sa6))
1010				errcnt++;
1011			if (!rpcb_set("tcp6", RPCPROG_MNT, 3, sa6))
1012				errcnt++;
1013		}
1014		if (errcnt)
1015			log(LOG_ERR, "couldn't register MOUNT/TCP service.");
1016	}
1017
1018	/* Register NFS exports with service discovery mechanism(s). */
1019
1020	/* bonjour */
1021	if (nfs_dns_service) {
1022		DNSServiceRefDeallocate(nfs_dns_service);
1023		nfs_dns_service = NULL;
1024	}
1025	if (config.bonjour && (config.tcp || config.udp)) {
1026		DNSServiceErrorType dserr;
1027		dserr = DNSServiceRegister(&nfs_dns_service, 0, 0, NULL,
1028				config.tcp ? "_nfs._tcp" : "_nfs._udp",
1029				config.bonjour_local_domain_only ? "local" : NULL,
1030				NULL, htons(config.port), 0, NULL, NULL, NULL);
1031		if (dserr != kDNSServiceErr_NoError) {
1032			log(LOG_ERR, "DNSServiceRegister(_nfs._tcp) failed with %d\n", dserr);
1033			nfs_dns_service = NULL;
1034		}
1035	}
1036}
1037
1038#pragma clang diagnostic push
1039#pragma clang diagnostic ignored "-Wformat-nonliteral"
1040
1041/*
1042 * our own little logging function...
1043 */
1044void
1045SYSLOG(int pri, const char *fmt, ...)
1046{
1047	va_list ap;
1048
1049	if (pri > LOG_LEVEL)
1050		return;
1051
1052	va_start(ap, fmt);
1053	if (log_to_stderr) {
1054		vfprintf(stderr, fmt, ap);
1055		fputc('\n', stderr);
1056		fflush(stderr);
1057	} else {
1058		vsyslog(pri, fmt, ap);
1059	}
1060	va_end(ap);
1061}
1062#pragma clang diagnostic pop
1063
1064/*
1065 * get the PID from the given pidfile
1066 */
1067static pid_t
1068get_pid(const char *path)
1069{
1070	char pidbuf[128], *pidend;
1071	int fd, len, rv;
1072	pid_t pid;
1073	struct flock lock;
1074
1075	if ((fd = open(path, O_RDONLY)) < 0) {
1076		DEBUG(5, "%s: %s (%d)", path, strerror(errno), errno);
1077		return (0);
1078	}
1079	len = sizeof(pidbuf) - 1;
1080	if ((len = read(fd, pidbuf, len)) < 0) {
1081		DEBUG(1, "%s: %s (%d)", path, strerror(errno), errno);
1082		return (0);
1083	}
1084
1085	/* parse PID */
1086	pidbuf[len] = '\0';
1087	pid = strtol(pidbuf, &pidend, 10);
1088	if (!len || (pid < 1)) {
1089		DEBUG(1, "%s: bogus pid: %s", path, pidbuf);
1090		return (0);
1091	}
1092
1093	/* check for lock on file by PID */
1094	lock.l_type = F_RDLCK;
1095	lock.l_whence = SEEK_SET;
1096	lock.l_start = 0;
1097	lock.l_len = 0;
1098	rv = fcntl(fd, F_GETLK, &lock);
1099	close(fd);
1100	if (rv != 0) {
1101		DEBUG(1, "%s: fcntl: %s (%d)", path, strerror(errno), errno);
1102		return (0);
1103	} else if (lock.l_type == F_UNLCK) {
1104		DEBUG(1, "%s: not locked", path);
1105		return (0);
1106	}
1107	return (pid);
1108}
1109
1110/*
1111 * get the PID of the running nfsd
1112 */
1113static pid_t
1114get_nfsd_pid(void)
1115{
1116	return (get_pid(_PATH_NFSD_PID));
1117}
1118
1119/*
1120 * send the running nfsd a SIGHUP to get it to update its config
1121 */
1122static void
1123signal_nfsd(int signal)
1124{
1125	pid_t pid = get_nfsd_pid();
1126	if (pid <= 0)
1127		errx(1, "nfsd not running?");
1128	if (kill(pid, signal) < 0)
1129		err(1, "kill(%d, %d)", pid, signal);
1130}
1131
1132/*
1133 * Check whether the given service appears to be permanently enabled (not Disabled).
1134 *
1135 * SMJobIsEnabled() actually returns the "loaded" status - the (permanently) enabled
1136 * status (i.e. the value of the Disabled key) is convolutedly returned via the last
1137 * argument (persistence).
1138 *
1139 * loaded persist -> Disabled
1140 *    0      0         0
1141 *    0      1         1
1142 *    1      0         1
1143 *    1      1         0
1144 */
1145static int
1146service_is_enabled(CFStringRef service)
1147{
1148	Boolean loaded = false, persistence = false;
1149	loaded = SMJobIsEnabled(kSMDomainSystemLaunchd, service, &persistence);
1150	return (!(loaded ^ persistence));
1151}
1152
1153/*
1154 * Check whether the given service appears to be loaded into launchd.
1155 */
1156static int
1157service_is_loaded(CFStringRef service)
1158{
1159	Boolean dummy;
1160	return (SMJobIsEnabled(kSMDomainSystemLaunchd, service, &dummy));
1161}
1162
1163/*
1164 * Check whether the nfsd service appears to be enabled.
1165 */
1166static int
1167nfsd_is_enabled(void)
1168{
1169	return service_is_enabled(CFSTR(_NFSD_SERVICE_LABEL));
1170}
1171
1172/*
1173 * Check whether the nfsd service is loaded.
1174 */
1175static int
1176nfsd_is_loaded(void)
1177{
1178	return service_is_loaded(CFSTR(_NFSD_SERVICE_LABEL));
1179}
1180
1181/*
1182 * Use launchctl to enable the nfsd service.
1183 */
1184static int
1185nfsd_enable(void)
1186{
1187	const char *const args[] = { _PATH_LAUNCHCTL, "load", "-w", _PATH_NFSD_PLIST, NULL };
1188	return safe_exec((char *const*)args, 0);
1189}
1190
1191/*
1192 * Use launchctl to disable the nfsd service.
1193 */
1194static int
1195nfsd_disable(void)
1196{
1197	const char *const args[] = { _PATH_LAUNCHCTL, "unload", "-w", _PATH_NFSD_PLIST, NULL };
1198	return safe_exec((char *const*)args, 0);
1199}
1200
1201/*
1202 * Use launchctl to load the nfsd service.
1203 */
1204static int
1205nfsd_load(void)
1206{
1207	const char *const args[] = { _PATH_LAUNCHCTL, "load", "-F", _PATH_NFSD_PLIST, NULL };
1208	return safe_exec((char *const*)args, 0);
1209}
1210
1211/*
1212 * Use launchctl to unload the nfsd service.
1213 */
1214static int
1215nfsd_unload(void)
1216{
1217	const char *const args[] = { _PATH_LAUNCHCTL, "unload", _PATH_NFSD_PLIST, NULL };
1218	return safe_exec((char *const*)args, 0);
1219}
1220
1221/*
1222 * Use launchctl to start the nfsd service.
1223 */
1224static int
1225nfsd_start(void)
1226{
1227	const char *const args[] = { _PATH_LAUNCHCTL, "start", _NFSD_SERVICE_LABEL, NULL };
1228	return safe_exec((char *const*)args, 0);
1229}
1230
1231/*
1232 * Use launchctl to stop the nfsd service.
1233 */
1234static int
1235nfsd_stop(void)
1236{
1237	const char *const args[] = { _PATH_LAUNCHCTL, "stop", _NFSD_SERVICE_LABEL, NULL };
1238	return safe_exec((char *const*)args, 0);
1239}
1240
1241/*
1242 * Check whether the nfsd service appears to be running.
1243 */
1244static int
1245nfsd_is_running(void)
1246{
1247	return (get_nfsd_pid() > 0);
1248}
1249
1250/*
1251 * run an external program
1252 */
1253static int
1254safe_exec(char *const argv[], int silent)
1255{
1256	posix_spawn_file_actions_t psfileact, *psfileactp = NULL;
1257	pid_t pid;
1258	int status;
1259	extern char **environ;
1260
1261	if (silent) {
1262		psfileactp = &psfileact;
1263		if ((status = posix_spawn_file_actions_init(psfileactp))) {
1264			log(LOG_ERR, "spawn init of %s failed: %s (%d)", argv[0], strerror(status), status);
1265			return (1);
1266		}
1267		posix_spawn_file_actions_addopen(psfileactp, STDIN_FILENO, "/dev/null", O_RDONLY, 0);
1268		posix_spawn_file_actions_addopen(psfileactp, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
1269		posix_spawn_file_actions_addopen(psfileactp, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
1270	}
1271	status = posix_spawn(&pid, argv[0], psfileactp, NULL, argv, environ);
1272	if (psfileactp)
1273		posix_spawn_file_actions_destroy(psfileactp);
1274	if (status) {
1275		log(LOG_ERR, "spawn of %s failed: %s (%d)", argv[0], strerror(status), status);
1276		return (1);
1277	}
1278	while ((waitpid(pid, &status, 0) == -1) && (errno == EINTR))
1279		usleep(1000);
1280	if (WIFSIGNALED(status)) {
1281		log(LOG_ERR, "%s aborted by signal %d", argv[0], WTERMSIG(status));
1282		return (1);
1283	} else if (WIFSTOPPED(status)) {
1284		log(LOG_ERR, "%s stopped by signal %d ?", argv[0], WSTOPSIG(status));
1285		return (1);
1286	} else if (WEXITSTATUS(status) && !silent) {
1287		log(LOG_ERR, "%s exited with status %d", argv[0], WEXITSTATUS(status));
1288	}
1289	return (WEXITSTATUS(status));
1290}
1291
1292/*
1293 * functions for managing rquotad
1294 */
1295static int
1296rquotad_is_loaded(void)
1297{
1298	launch_data_t msg, resp;
1299	int rv = 0;
1300
1301	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1302	if (!msg)
1303		return (0);
1304	launch_data_dict_insert(msg, launch_data_new_string(_RQUOTAD_SERVICE_LABEL), LAUNCH_KEY_GETJOB);
1305
1306	resp = launch_msg(msg);
1307	if (resp) {
1308		if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY)
1309			rv = 1;
1310		launch_data_free(resp);
1311	} else {
1312		syslog(LOG_ERR, "launch_msg(): %m");
1313	}
1314
1315	launch_data_free(msg);
1316	return (rv);
1317}
1318
1319static int
1320rquotad_load(void)
1321{
1322	launch_data_t msg, job, args, resp;
1323	int rv = 1;
1324
1325	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1326	job = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1327	args = launch_data_alloc(LAUNCH_DATA_ARRAY);
1328	if (!msg || !job || !args) {
1329		if (msg) launch_data_free(msg);
1330		if (job) launch_data_free(job);
1331		if (args) launch_data_free(args);
1332		return (1);
1333	}
1334	launch_data_array_set_index(args, launch_data_new_string(_PATH_RQUOTAD), 0);
1335	launch_data_dict_insert(job, launch_data_new_string(_RQUOTAD_SERVICE_LABEL), LAUNCH_JOBKEY_LABEL);
1336	launch_data_dict_insert(job, launch_data_new_bool(FALSE), LAUNCH_JOBKEY_ONDEMAND);
1337	launch_data_dict_insert(job, args, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
1338	launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB);
1339
1340	resp = launch_msg(msg);
1341	if (!resp) {
1342		rv = errno;
1343	} else {
1344		if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)
1345			rv = launch_data_get_errno(resp);
1346		launch_data_free(resp);
1347	}
1348
1349	launch_data_free(msg);
1350	return (rv);
1351}
1352
1353static int
1354rquotad_service_start(void)
1355{
1356	launch_data_t msg, resp;
1357	int rv = 1;
1358
1359	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1360	if (!msg)
1361		return (1);
1362	launch_data_dict_insert(msg, launch_data_new_string(_RQUOTAD_SERVICE_LABEL), LAUNCH_KEY_STARTJOB);
1363
1364	resp = launch_msg(msg);
1365	if (!resp) {
1366		rv = errno;
1367	} else {
1368		if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)
1369			rv = launch_data_get_errno(resp);
1370		launch_data_free(resp);
1371	}
1372
1373	launch_data_free(msg);
1374	return (rv);
1375}
1376
1377static int
1378rquotad_start(void)
1379{
1380	if (get_pid(_PATH_RQUOTAD_PID) > 0)
1381		return (0);
1382	else if (rquotad_is_loaded())
1383		return (rquotad_service_start());
1384	return (rquotad_load());
1385}
1386
1387static int
1388rquotad_stop(void)
1389{
1390	launch_data_t msg, resp;
1391	int rv = 1;
1392
1393	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1394	if (!msg)
1395		return (1);
1396	launch_data_dict_insert(msg, launch_data_new_string(_RQUOTAD_SERVICE_LABEL), LAUNCH_KEY_REMOVEJOB);
1397
1398	resp = launch_msg(msg);
1399	if (!resp) {
1400		rv = errno;
1401	} else {
1402		if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)
1403			rv = launch_data_get_errno(resp);
1404		launch_data_free(resp);
1405	}
1406
1407	launch_data_free(msg);
1408	return (rv);
1409}
1410
1411