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