/* * Copyright (c) 2002-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* $NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $ */ /* $FreeBSD: src/usr.sbin/rpc.lockd/lockd.c,v 1.13 2002/04/11 07:19:30 alfred Exp $ */ /* * Copyright (c) 1995 * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the FreeBSD project * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #ifndef lint __RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $"); #endif /* * main() function for NFS lock daemon. Most of the code in this * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x. * * The actual program logic is in the file lock_proc.c */ /* make sure we can handle lots of file descriptors */ #include #define FD_SETSIZE OPEN_MAX #define _DARWIN_UNLIMITED_SELECT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lockd.h" #include int bindresvport_sa(int sd, struct sockaddr *sa); int _rpcsvcdirty = 0; struct pidfh *pfh = NULL; const struct nfs_conf_lockd config_defaults = { 45, /* grace_period */ 60, /* host_monitor_cache_timeout */ 0, /* port */ 180, /* shutdown_delay_client */ 180, /* shutdown_delay_server */ 0, /* verbose */ }; struct nfs_conf_lockd config; int udpport, tcpport; int grace_expired; int nsm_state; pid_t server_pid = -1; struct mon mon_host; time_t currsec; void init_nsm(void); void nlm_prog_0(struct svc_req *, SVCXPRT *); void nlm_prog_1(struct svc_req *, SVCXPRT *); void nlm_prog_3(struct svc_req *, SVCXPRT *); void nlm_prog_4(struct svc_req *, SVCXPRT *); void usage(void); static int config_read(struct nfs_conf_lockd *); static void sigalarm_grace_period_handler(void); static void handle_sigchld(int sig); void my_svc_run(void); static int statd_is_loaded(void); static int statd_load(void); static int statd_service_start(void); const char *transports[] = { "udp", "tcp", "udp6", "tcp6" }; /* * This is the start up of lockd. Lockd has two functions one is to receive * request from the kernel and forward them to the appropriate servers, i.e., * the client side. The other is to handle request from the remote clients, i.e., * the server side. Note the client request use asynchronous rpc (_MSG procedures), * so the server side will get the reply and then use an nfs system call to return * the result to the kernel. We use two processes, one for the client and one for * the server. This is due to the fact that rpc library is mt hostile. At a later * date we should fix that. The client forks off the server since we're started by * lockd and the client gets its receive right from lockd. When nfsd starts up it * will send a "ping" request on a host port, launchd will then start lockd. * * This daemon is to be started by launchd, as such it follows the following * launchd rules: * We don't: * call daemon(3) * call fork and having the parent process exit * change uids or gids. * set up the current working directory or chroot. * set the session id * change stdio to /dev/null. * call setrusage(2) * call setpriority(2) * Ignore SIGTERM. * We are launched on demand * and we catch SIGTERM to exit cleanly. * * In practice daemonizing in the classic unix sense would probably be ok * since we get invoke by traffic on a task_special_port, but we will play * by the rules, its even easier to boot. */ int main(argc, argv) int argc; char **argv; { SVCXPRT *transp; struct sockaddr_in inetaddr; socklen_t socklen; int ch, sockfd, rv; struct sigaction sa; struct rlimit rlp; pid_t child, pid; sigset_t waitset, osigset; int server_sig; config = config_defaults; config_read(&config); while ((ch = getopt(argc, argv, "d:g:x:")) != (-1)) { switch (ch) { case 'd': config.verbose = atoi(optarg); break; case 'g': config.grace_period = atoi(optarg); break; case 'x': config.host_monitor_cache_timeout = atoi(optarg); break; default: case '?': usage(); /* NOTREACHED */ } } if (geteuid()) { /* This command allowed only to root */ fprintf(stderr, "Sorry. You are not superuser\n"); exit(1); } /* Install signal handler to do cleanup */ signal(SIGINT, handle_sig_cleanup); signal(SIGTERM, handle_sig_cleanup); signal(SIGHUP, handle_sig_cleanup); signal(SIGQUIT, handle_sig_cleanup); openlog("rpc.lockd", LOG_CONS | LOG_PID | ((config.verbose == 99) ? LOG_PERROR : 0), LOG_DAEMON); /* claim PID file */ pfh = pidfile_open(_PATH_LOCKD_PID, 0644, &pid); if (pfh == NULL) { syslog(LOG_ERR, "can't open lockd pidfile: %s (%d)", strerror(errno), errno); if (errno == EEXIST) { syslog(LOG_ERR, "lockd already running, pid: %d", pid); exit(0); } exit(2); } if (pidfile_write(pfh) == -1) syslog(LOG_WARNING, "can't write to lockd pidfile: %s (%d)", strerror(errno), errno); if (config.verbose) syslog(LOG_INFO, "lockd starting, debug level %d", config.verbose); else syslog(LOG_INFO, "lockd starting"); /* make sure statd is running */ if ((rv = statd_start())) syslog(LOG_WARNING, "unable to start statd %d", rv); /* Install signal handler to collect exit status of child processes */ sa.sa_handler = handle_sigchld; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGCHLD); sa.sa_flags = SA_RESTART; sigaction(SIGCHLD, &sa, NULL); /* * Create a separate process, the client code is really a separate * daemon that shares a lot of code. We let the server be the child. * The reason for this is that child will get the receive right from * launchd and it is that process that launchd should be monitoring * We mask SIGUSR1 and then pause in the parent. When the child is * ready it will signal the parent to continue. In this way we know * that if we send a request out to some server, we have a server * process up to handle the reply. */ sigemptyset(&waitset); sigaddset(&waitset, SIGUSR1); sigaddset(&waitset, SIGCHLD); sigaddset(&waitset, SIGINT); sigaddset(&waitset, SIGTERM); sigprocmask(SIG_BLOCK, &waitset, &osigset); switch (child = fork()) { case -1: syslog(LOG_ERR, "Could not fork server"); handle_sig_cleanup(0); /*NOTREACHED*/ case 0: /* Server doesn't need to block */ sigprocmask(SIG_SETMASK, &osigset, NULL); break; default: /* we're the parent and the client */ server_pid = child; /* Wait for the server to be up */ sigwait(&waitset, &server_sig); if (server_sig != SIGUSR1) { syslog(LOG_ERR, "Lockd got unexpected signal %d\n", server_sig); handle_sig_cleanup(0); } sigprocmask(SIG_SETMASK, &osigset, NULL); /* Reset signal mask */ client_mach_request(); /* We should never return, but if we do kill our other half */ syslog(LOG_ERR, "Lockd: client_mach_request(), returned unexpectedly"); handle_sig_cleanup(0); /*NOTREACHED*/ } /* We're the child and the server */ pmap_unset(NLM_PROG, NLM_SM); pmap_unset(NLM_PROG, NLM_VERS); pmap_unset(NLM_PROG, NLM_VERSX); pmap_unset(NLM_PROG, NLM_VERS4); /* parent cleans up the pid file */ pfh = NULL; if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "can't create UDP socket: %s (%d)", strerror(errno), errno); exit(1); } inetaddr.sin_family = AF_INET; inetaddr.sin_addr.s_addr = INADDR_ANY; inetaddr.sin_port = htons(config.port); inetaddr.sin_len = sizeof(inetaddr); if (bindresvport_sa(sockfd, (struct sockaddr *) &inetaddr) < 0) { syslog(LOG_ERR, "can't bind UDP addr: %s (%d)", strerror(errno), errno); exit(1); } socklen = sizeof(inetaddr); if (getsockname(sockfd, (struct sockaddr *) &inetaddr, &socklen)) syslog(LOG_ERR, "can't getsockname on UDP socket: %s (%d)", strerror(errno), errno); else udpport = ntohs(inetaddr.sin_port); transp = svcudp_create(sockfd); if (transp == NULL) { syslog(LOG_ERR, "cannot create UDP service"); exit(1); } if (!svc_register(transp, NLM_PROG, NLM_SM, nlm_prog_0, IPPROTO_UDP)) { syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_SM, udp)"); exit(1); } if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_UDP)) { syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS, udp)"); exit(1); } if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_UDP)) { syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERSX, udp)"); exit(1); } if (!svc_register(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, IPPROTO_UDP)) { syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS4, udp)"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "can't create TCP socket: %s (%d)", strerror(errno), errno); exit(1); } inetaddr.sin_family = AF_INET; inetaddr.sin_addr.s_addr = INADDR_ANY; inetaddr.sin_port = htons(config.port); inetaddr.sin_len = sizeof(inetaddr); if (bindresvport_sa(sockfd, (struct sockaddr *) &inetaddr) < 0) { syslog(LOG_ERR, "can't bind TCP addr: %s (%d)", strerror(errno), errno); exit(1); } socklen = sizeof(inetaddr); if (getsockname(sockfd, (struct sockaddr *) &inetaddr, &socklen)) syslog(LOG_ERR, "can't getsockname on TCP socket: %s (%d)", strerror(errno), errno); else tcpport = ntohs(inetaddr.sin_port); transp = svctcp_create(sockfd, 0, 0); if (transp == NULL) { syslog(LOG_ERR, "cannot create TCP service"); exit(1); } if (!svc_register(transp, NLM_PROG, NLM_SM, nlm_prog_0, IPPROTO_TCP)) { syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_SM, tcp)"); exit(1); } if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_TCP)) { syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS, tcp)"); exit(1); } if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_TCP)) { syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERSX, tcp)"); exit(1); } if (!svc_register(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, IPPROTO_TCP)) { syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS4, tcp)"); exit(1); } init_nsm(); /* raise our resource limits as far as they can go */ if (getrlimit(RLIMIT_NOFILE, &rlp)) { syslog(LOG_WARNING, "getrlimit(RLIMIT_NOFILE) failed: %s", strerror(errno)); } else { rlp.rlim_cur = MIN(rlp.rlim_max, OPEN_MAX); if (setrlimit(RLIMIT_NOFILE, &rlp)) { syslog(LOG_WARNING, "setrlimit(RLIMIT_NOFILE) failed: %s", strerror(errno)); } } /* set up grace period alarm */ sa.sa_handler = (sig_t) sigalarm_grace_period_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESETHAND; /* should only happen once */ sa.sa_flags |= SA_RESTART; if (sigaction(SIGALRM, &sa, NULL) != 0) { syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s", strerror(errno)); exit(1); } if (config.grace_period > 0) { grace_expired = 0; alarm(config.grace_period); } else { grace_expired = 1; } /* Signal the parent (client process) that its ok to start */ kill(getppid(), SIGUSR1); my_svc_run(); /* Should never return */ exit(1); } void sigalarm_grace_period_handler(void) { grace_expired = 1; } void usage() { errx(1, "usage: rpc.lockd [-d ] [-g ] " " [-x ] [-w]"); } /* * init_nsm -- * Reset the NSM state-of-the-world and acquire its state. */ void init_nsm(void) { enum clnt_stat ret; sm_stat mstat; char localhost[] = "localhost"; int attempt = 0; /* setup constant data for SM_MON calls */ mon_host.mon_id.my_id.my_name = localhost; mon_host.mon_id.my_id.my_prog = NLM_PROG; mon_host.mon_id.my_id.my_vers = NLM_SM; mon_host.mon_id.my_id.my_proc = NLM_SM_NOTIFY; /* bsdi addition */ /* * !!! * The statd program must already be registered when lockd runs. * If we have a problem contacting statd, pause and try again a * number of times in case statd is just slow in coming up. */ do { ret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON_ALL, (xdrproc_t)xdr_my_id, &mon_host.mon_id.my_id, (xdrproc_t)xdr_sm_stat, &mstat); if (ret) { syslog(LOG_WARNING, "can't contact statd, %lu %s", SM_PROG, clnt_sperrno(ret)); if (++attempt < 20) { /* attempt to start it again */ statd_start(); sleep(attempt); continue; } } break; } while (1); if (ret != 0) { syslog(LOG_ERR, "server init_nsm failed! %lu %s", SM_PROG, clnt_sperrno(ret)); exit(1); } nsm_state = mstat.state; } /* * handle_sig_cleanup * * Purpose: on signal, kill server child and do pid file cleanup * Returns: Nothing */ void handle_sig_cleanup(int sig) { pid_t pid; int status; if (server_pid != -1) { pid = server_pid; server_pid = -1; kill(pid, SIGTERM); wait4(pid, &status, 0, NULL); } else { alarm(1); /* XXX 5028243 in case pmap_unset() gets hung up during shutdown */ pmap_unset(NLM_PROG, NLM_SM); pmap_unset(NLM_PROG, NLM_VERS); pmap_unset(NLM_PROG, NLM_VERSX); pmap_unset(NLM_PROG, NLM_VERS4); } if (pfh) pidfile_remove(pfh); exit((sig == SIGTERM) ? 0 : 1); } /* * handle_sigchld * * Exit if we catch the lockd server child process dying. */ static void handle_sigchld(int sig __unused) { pid_t pid; int status; pid = wait4(-1, &status, WNOHANG, NULL); if ((server_pid != -1) && (pid == server_pid)) { if (status) syslog(LOG_ERR, "lockd server %d failed with status %d", pid, WEXITSTATUS(status)); handle_sig_cleanup(0); /*NOTREACHED*/ } } /* * The RPC service run loop */ void my_svc_run(void) { fd_set readfds; struct timeval timeout; struct timeval now; int error; int hashosts = 0; int tsize = 0; struct timeval *top; for( ;; ) { timeout.tv_sec = config.host_monitor_cache_timeout + 1; timeout.tv_usec = 0; tsize = getdtablesize(); bcopy(&svc_fdset, &readfds, sizeof(svc_fdset)); /* * If there are any expired hosts then sleep with a * timeout to expire them. */ if (hashosts && (timeout.tv_sec >= 0)) top = &timeout; else top = NULL; error = select(tsize, &readfds, NULL, NULL, top); if (error == -1) { if (errno == EINTR) continue; perror("rpc.lockd: my_svc_run: select failed"); return; } gettimeofday(&now, NULL); currsec = now.tv_sec; if (error > 0) svc_getreqset(&readfds); if ((config.verbose > 3) && (error == 0)) fprintf(stderr, "my_svc_run: select timeout\n"); hashosts = expire_lock_hosts(); } } /* * read the lockd values from nfs.conf */ static int config_read(struct nfs_conf_lockd *conf) { FILE *f; size_t len, linenum = 0; char *line, *p, *key, *value; long val; if (!(f = fopen(_PATH_NFS_CONF, "r"))) { if (errno != ENOENT) syslog(LOG_WARNING, "%s", _PATH_NFS_CONF); return (1); } for (; (line = fparseln(f, &len, &linenum, NULL, 0)); free(line)) { if (len <= 0) continue; /* trim trailing whitespace */ p = line + len - 1; while ((p > line) && isspace(*p)) *p-- = '\0'; /* find key start */ key = line; while (isspace(*key)) key++; /* find equals/value */ value = p = strchr(line, '='); if (p) /* trim trailing whitespace on key */ do { *p-- = '\0'; } while ((p > line) && isspace(*p)); /* find value start */ if (value) do { value++; } while (isspace(*value)); /* all lockd keys start with "nfs.lockd." */ if (strncmp(key, "nfs.lockd.", 10)) { if (config.verbose) syslog(LOG_DEBUG, "%4ld %s=%s\n", linenum, key, value ? value : ""); continue; } val = !value ? 1 : strtol(value, NULL, 0); if (config.verbose) syslog(LOG_DEBUG, "%4ld %s=%s (%d)\n", linenum, key, value ? value : "", val); if (!strcmp(key, "nfs.lockd.grace_period")) { conf->grace_period = val; } else if (!strcmp(key, "nfs.lockd.host_monitor_cache_timeout")) { conf->host_monitor_cache_timeout = val; } else if (!strcmp(key, "nfs.lockd.port")) { conf->port = val; } else if (!strcmp(key, "nfs.lockd.shutdown_delay_client")) { conf->shutdown_delay_client = val; } else if (!strcmp(key, "nfs.lockd.shutdown_delay_server")) { conf->shutdown_delay_server = val; } else if (!strcmp(key, "nfs.lockd.verbose")) { conf->verbose = val; } else { if (config.verbose) syslog(LOG_DEBUG, "ignoring unknown config value: %4ld %s=%s\n", linenum, key, value ? value : ""); } } fclose(f); return (0); } /* * get the PID of the running statd */ static pid_t get_statd_pid(void) { char pidbuf[128], *pidend; int fd, len, rv; pid_t pid; struct flock lock; if ((fd = open(_PATH_STATD_PID, O_RDONLY)) < 0) { if (config.verbose) syslog(LOG_DEBUG, "%s: %s (%d)", _PATH_STATD_PID, strerror(errno), errno); return (0); } len = sizeof(pidbuf) - 1; if ((len = read(fd, pidbuf, len)) < 0) { if (config.verbose) syslog(LOG_DEBUG, "%s: %s (%d)", _PATH_STATD_PID, strerror(errno), errno); return (0); } /* parse PID */ pidbuf[len] = '\0'; pid = strtol(pidbuf, &pidend, 10); if (!len || (pid < 1)) { if (config.verbose) syslog(LOG_DEBUG, "%s: bogus pid: %s", _PATH_STATD_PID, pidbuf); return (0); } /* check for lock on file by PID */ lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; rv = fcntl(fd, F_GETLK, &lock); close(fd); if (rv != 0) { if (config.verbose) syslog(LOG_DEBUG, "%s: fcntl: %s (%d)", _PATH_STATD_PID, strerror(errno), errno); return (0); } else if (lock.l_type == F_UNLCK) { if (config.verbose) syslog(LOG_DEBUG, "%s: not locked\n", _PATH_STATD_PID); return (0); } return (pid); } static int statd_is_running(void) { return (get_statd_pid() > 0); } static int statd_is_loaded(void) { launch_data_t msg, resp; int rv = 0; msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); if (!msg) return (0); launch_data_dict_insert(msg, launch_data_new_string(_STATD_SERVICE_LABEL), LAUNCH_KEY_GETJOB); resp = launch_msg(msg); if (resp) { if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) rv = 1; launch_data_free(resp); } else { syslog(LOG_ERR, "launch_msg(): %m"); } launch_data_free(msg); return (rv); } static int statd_load(void) { launch_data_t msg, job, args, resp; int rv = 1; msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); job = launch_data_alloc(LAUNCH_DATA_DICTIONARY); args = launch_data_alloc(LAUNCH_DATA_ARRAY); if (!msg || !job || !args) { if (msg) launch_data_free(msg); if (job) launch_data_free(job); if (args) launch_data_free(args); return (1); } launch_data_array_set_index(args, launch_data_new_string(_PATH_STATD), 0); launch_data_dict_insert(job, launch_data_new_string(_STATD_SERVICE_LABEL), LAUNCH_JOBKEY_LABEL); launch_data_dict_insert(job, launch_data_new_bool(FALSE), LAUNCH_JOBKEY_ONDEMAND); launch_data_dict_insert(job, launch_data_new_bool(TRUE), LAUNCH_JOBKEY_HOPEFULLYEXITSLAST); launch_data_dict_insert(job, args, LAUNCH_JOBKEY_PROGRAMARGUMENTS); launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB); resp = launch_msg(msg); if (!resp) { rv = errno; } else { if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) rv = launch_data_get_errno(resp); launch_data_free(resp); } launch_data_free(msg); return (rv); } static int statd_service_start(void) { launch_data_t msg, resp; int rv = 1; msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); if (!msg) return (1); launch_data_dict_insert(msg, launch_data_new_string(_STATD_SERVICE_LABEL), LAUNCH_KEY_STARTJOB); resp = launch_msg(msg); if (!resp) { rv = errno; } else { if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) rv = launch_data_get_errno(resp); launch_data_free(resp); } launch_data_free(msg); return (rv); } int statd_start(void) { struct timespec ts; int rv; if (statd_is_running()) rv = 0; else if (statd_is_loaded()) rv = statd_service_start(); else rv = statd_load(); /* sleep a little to give statd/portmap a chance to start */ /* (better to sleep 100ms than to timeout on portmap calls) */ ts.tv_sec = 0; ts.tv_nsec = 100*1000*1000; nanosleep(&ts, NULL); return (rv); } int statd_stop(void) { launch_data_t msg, resp; int rv = 1; msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); if (!msg) return (1); launch_data_dict_insert(msg, launch_data_new_string(_STATD_SERVICE_LABEL), LAUNCH_KEY_REMOVEJOB); resp = launch_msg(msg); if (!resp) { rv = errno; } else { if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) rv = launch_data_get_errno(resp); launch_data_free(resp); } launch_data_free(msg); return (rv); }