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