summaryrefslogtreecommitdiffstats
path: root/usr.sbin/rpc.statd
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/rpc.statd')
-rw-r--r--usr.sbin/rpc.statd/Makefile26
-rw-r--r--usr.sbin/rpc.statd/Makefile.depend32
-rw-r--r--usr.sbin/rpc.statd/file.c361
-rw-r--r--usr.sbin/rpc.statd/procs.c436
-rw-r--r--usr.sbin/rpc.statd/rpc.statd.8137
-rw-r--r--usr.sbin/rpc.statd/statd.c655
-rw-r--r--usr.sbin/rpc.statd/statd.h111
-rw-r--r--usr.sbin/rpc.statd/test.c144
8 files changed, 1902 insertions, 0 deletions
diff --git a/usr.sbin/rpc.statd/Makefile b/usr.sbin/rpc.statd/Makefile
new file mode 100644
index 0000000..43504e4
--- /dev/null
+++ b/usr.sbin/rpc.statd/Makefile
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+PROG= rpc.statd
+MAN= rpc.statd.8
+SRCS= file.c sm_inter_svc.c sm_inter.h statd.c procs.c
+
+CFLAGS+= -I.
+WARNS?= 2
+
+LIBADD= rpcsvc
+
+CLEANFILES= sm_inter_svc.c sm_inter.h
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/sm_inter.x
+RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -L -C
+
+sm_inter_svc.c: ${RPCSRC}
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+sm_inter.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+test: test.c
+ cc -o test test.c -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.statd/Makefile.depend b/usr.sbin/rpc.statd/Makefile.depend
new file mode 100644
index 0000000..a356b97
--- /dev/null
+++ b/usr.sbin/rpc.statd/Makefile.depend
@@ -0,0 +1,32 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ gnu/lib/libgcc \
+ include \
+ include/arpa \
+ include/rpc \
+ include/rpcsvc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/librpcsvc \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+file.o: sm_inter.h
+file.po: sm_inter.h
+procs.o: sm_inter.h
+procs.po: sm_inter.h
+sm_inter_svc.o: sm_inter.h
+sm_inter_svc.o: sm_inter_svc.c
+sm_inter_svc.po: sm_inter.h
+sm_inter_svc.po: sm_inter_svc.c
+statd.o: sm_inter.h
+statd.po: sm_inter.h
+.endif
diff --git a/usr.sbin/rpc.statd/file.c b/usr.sbin/rpc.statd/file.c
new file mode 100644
index 0000000..0625e30
--- /dev/null
+++ b/usr.sbin/rpc.statd/file.c
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h> /* For mmap() */
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include "statd.h"
+
+FileLayout *status_info; /* Pointer to the mmap()ed status file */
+static int status_fd; /* File descriptor for the open file */
+static off_t status_file_len; /* Current on-disc length of file */
+
+/* sync_file --------------------------------------------------------------- */
+/*
+ Purpose: Packaged call of msync() to flush changes to mmap()ed file
+ Returns: Nothing. Errors to syslog.
+*/
+
+void sync_file(void)
+{
+ if (msync((void *)status_info, 0, 0) < 0)
+ {
+ syslog(LOG_ERR, "msync() failed: %s", strerror(errno));
+ }
+}
+
+/* find_host -------------------------------------------------------------- */
+/*
+ Purpose: Find the entry in the status file for a given host
+ Returns: Pointer to that entry in the mmap() region, or NULL.
+ Notes: Also creates entries if requested.
+ Failure to create also returns NULL.
+*/
+
+HostInfo *find_host(char *hostname, int create)
+{
+ HostInfo *hp;
+ HostInfo *spare_slot = NULL;
+ HostInfo *result = NULL;
+ struct addrinfo *ai1, *ai2;
+ int i;
+
+ if (getaddrinfo(hostname, NULL, NULL, &ai1) != 0)
+ ai1 = NULL;
+ for (i = 0, hp = status_info->hosts; i < status_info->noOfHosts; i++, hp++)
+ {
+ if (!strncasecmp(hostname, hp->hostname, SM_MAXSTRLEN))
+ {
+ result = hp;
+ break;
+ }
+ if (hp->hostname[0] &&
+ getaddrinfo(hp->hostname, NULL, NULL, &ai2) != 0)
+ ai2 = NULL;
+ if (ai1 && ai2)
+ {
+ struct addrinfo *p1, *p2;
+ for (p1 = ai1; !result && p1; p1 = p1->ai_next)
+ {
+ for (p2 = ai2; !result && p2; p2 = p2->ai_next)
+ {
+ if (p1->ai_family == p2->ai_family
+ && p1->ai_addrlen == p2->ai_addrlen
+ && !memcmp(p1->ai_addr, p2->ai_addr, p1->ai_addrlen))
+ {
+ result = hp;
+ break;
+ }
+ }
+ }
+ if (result)
+ break;
+ }
+ if (ai2)
+ freeaddrinfo(ai2);
+ if (!spare_slot && !hp->monList && !hp->notifyReqd)
+ spare_slot = hp;
+ }
+ if (ai1)
+ freeaddrinfo(ai1);
+
+ /* Return if entry found, or if not asked to create one. */
+ if (result || !create) return (result);
+
+ /* Now create an entry, using the spare slot if one was found or */
+ /* adding to the end of the list otherwise, extending file if reqd */
+ if (!spare_slot)
+ {
+ off_t desired_size;
+ spare_slot = &status_info->hosts[status_info->noOfHosts];
+ desired_size = ((char*)spare_slot - (char*)status_info) + sizeof(HostInfo);
+ if (desired_size > status_file_len)
+ {
+ /* Extend file by writing 1 byte of junk at the desired end pos */
+ lseek(status_fd, desired_size - 1, SEEK_SET);
+ i = write(status_fd, &i, 1);
+ if (i < 1)
+ {
+ syslog(LOG_ERR, "Unable to extend status file");
+ return (NULL);
+ }
+ status_file_len = desired_size;
+ }
+ status_info->noOfHosts++;
+ }
+
+ /* Initialise the spare slot that has been found/created */
+ /* Note that we do not msync(), since the caller is presumed to be */
+ /* about to modify the entry further */
+ memset(spare_slot, 0, sizeof(HostInfo));
+ strncpy(spare_slot->hostname, hostname, SM_MAXSTRLEN);
+ return (spare_slot);
+}
+
+/* init_file -------------------------------------------------------------- */
+/*
+ Purpose: Open file, create if necessary, initialise it.
+ Returns: Nothing - exits on error
+ Notes: Called before process becomes daemon, hence logs to
+ stderr rather than syslog.
+ Opens the file, then mmap()s it for ease of access.
+ Also performs initial clean-up of the file, zeroing
+ monitor list pointers, setting the notifyReqd flag in
+ all hosts that had a monitor list, and incrementing
+ the state number to the next even value.
+*/
+
+void init_file(const char *filename)
+{
+ int new_file = FALSE;
+ char buf[HEADER_LEN];
+ int i;
+
+ /* try to open existing file - if not present, create one */
+ status_fd = open(filename, O_RDWR);
+ if ((status_fd < 0) && (errno == ENOENT))
+ {
+ status_fd = open(filename, O_RDWR | O_CREAT, 0644);
+ new_file = TRUE;
+ }
+ if (status_fd < 0)
+ errx(1, "unable to open status file %s", filename);
+
+ /* File now open. mmap() it, with a generous size to allow for */
+ /* later growth, where we will extend the file but not re-map it. */
+ status_info = (FileLayout *)
+ mmap(NULL, 0x10000000, PROT_READ | PROT_WRITE, MAP_SHARED, status_fd, 0);
+
+ if (status_info == (FileLayout *) MAP_FAILED)
+ err(1, "unable to mmap() status file");
+
+ status_file_len = lseek(status_fd, 0L, SEEK_END);
+
+ /* If the file was not newly created, validate the contents, and if */
+ /* defective, re-create from scratch. */
+ if (!new_file)
+ {
+ if ((status_file_len < HEADER_LEN) || (status_file_len
+ < (HEADER_LEN + sizeof(HostInfo) * status_info->noOfHosts)) )
+ {
+ warnx("status file is corrupt");
+ new_file = TRUE;
+ }
+ }
+
+ /* Initialisation of a new, empty file. */
+ if (new_file)
+ {
+ memset(buf, 0, sizeof(buf));
+ lseek(status_fd, 0L, SEEK_SET);
+ write(status_fd, buf, HEADER_LEN);
+ status_file_len = HEADER_LEN;
+ }
+ else
+ {
+ /* Clean-up of existing file - monitored hosts will have a pointer */
+ /* to a list of clients, which refers to memory in the previous */
+ /* incarnation of the program and so are meaningless now. These */
+ /* pointers are zeroed and the fact that the host was previously */
+ /* monitored is recorded by setting the notifyReqd flag, which will */
+ /* in due course cause a SM_NOTIFY to be sent. */
+ /* Note that if we crash twice in quick succession, some hosts may */
+ /* already have notifyReqd set, where we didn't manage to notify */
+ /* them before the second crash occurred. */
+ for (i = 0; i < status_info->noOfHosts; i++)
+ {
+ HostInfo *this_host = &status_info->hosts[i];
+
+ if (this_host->monList)
+ {
+ this_host->notifyReqd = TRUE;
+ this_host->monList = NULL;
+ }
+ }
+ /* Select the next higher even number for the state counter */
+ status_info->ourState = (status_info->ourState + 2) & 0xfffffffe;
+/*???????******/ status_info->ourState++;
+ }
+}
+
+/* notify_one_host --------------------------------------------------------- */
+/*
+ Purpose: Perform SM_NOTIFY procedure at specified host
+ Returns: TRUE if success, FALSE if failed.
+*/
+
+static int notify_one_host(char *hostname)
+{
+ struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
+ CLIENT *cli;
+ char dummy;
+ stat_chge arg;
+ char our_hostname[SM_MAXSTRLEN+1];
+
+ gethostname(our_hostname, sizeof(our_hostname));
+ our_hostname[SM_MAXSTRLEN] = '\0';
+ arg.mon_name = our_hostname;
+ arg.state = status_info->ourState;
+
+ if (debug) syslog (LOG_DEBUG, "Sending SM_NOTIFY to host %s from %s", hostname, our_hostname);
+
+ cli = clnt_create(hostname, SM_PROG, SM_VERS, "udp");
+ if (!cli)
+ {
+ syslog(LOG_ERR, "Failed to contact host %s%s", hostname,
+ clnt_spcreateerror(""));
+ return (FALSE);
+ }
+
+ if (clnt_call(cli, SM_NOTIFY, (xdrproc_t)xdr_stat_chge, &arg,
+ (xdrproc_t)xdr_void, &dummy, timeout)
+ != RPC_SUCCESS)
+ {
+ syslog(LOG_ERR, "Failed to contact rpc.statd at host %s", hostname);
+ clnt_destroy(cli);
+ return (FALSE);
+ }
+
+ clnt_destroy(cli);
+ return (TRUE);
+}
+
+/* notify_hosts ------------------------------------------------------------ */
+/*
+ Purpose: Send SM_NOTIFY to all hosts marked as requiring it
+ Returns: Nothing, immediately - forks a process to do the work.
+ Notes: Does nothing if there are no monitored hosts.
+ Called after all the initialisation has been done -
+ logs to syslog.
+*/
+
+void notify_hosts(void)
+{
+ int i;
+ int attempts;
+ int work_to_do = FALSE;
+ HostInfo *hp;
+ pid_t pid;
+
+ /* First check if there is in fact any work to do. */
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
+ {
+ if (hp->notifyReqd)
+ {
+ work_to_do = TRUE;
+ break;
+ }
+ }
+
+ if (!work_to_do) return; /* No work found */
+
+ pid = fork();
+ if (pid == -1)
+ {
+ syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
+ return;
+ }
+ if (pid) return;
+
+ /* Here in the child process. We continue until all the hosts marked */
+ /* as requiring notification have been duly notified. */
+ /* If one of the initial attempts fails, we sleep for a while and */
+ /* have another go. This is necessary because when we have crashed, */
+ /* (eg. a power outage) it is quite possible that we won't be able to */
+ /* contact all monitored hosts immediately on restart, either because */
+ /* they crashed too and take longer to come up (in which case the */
+ /* notification isn't really required), or more importantly if some */
+ /* router etc. needed to reach the monitored host has not come back */
+ /* up yet. In this case, we will be a bit late in re-establishing */
+ /* locks (after the grace period) but that is the best we can do. */
+ /* We try 10 times at 5 sec intervals, 10 more times at 1 minute */
+ /* intervals, then 24 more times at hourly intervals, finally */
+ /* giving up altogether if the host hasn't come back to life after */
+ /* 24 hours. */
+
+ for (attempts = 0; attempts < 44; attempts++)
+ {
+ work_to_do = FALSE; /* Unless anything fails */
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
+ {
+ if (hp->notifyReqd)
+ {
+ if (notify_one_host(hp->hostname))
+ {
+ hp->notifyReqd = FALSE;
+ sync_file();
+ }
+ else work_to_do = TRUE;
+ }
+ }
+ if (!work_to_do) break;
+ if (attempts < 10) sleep(5);
+ else if (attempts < 20) sleep(60);
+ else sleep(60*60);
+ }
+ exit(0);
+}
+
+
diff --git a/usr.sbin/rpc.statd/procs.c b/usr.sbin/rpc.statd/procs.c
new file mode 100644
index 0000000..f6067e7
--- /dev/null
+++ b/usr.sbin/rpc.statd/procs.c
@@ -0,0 +1,436 @@
+/*
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <vis.h>
+#include <netdb.h> /* for getaddrinfo() */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "statd.h"
+
+static const char *
+from_addr(saddr)
+ struct sockaddr *saddr;
+{
+ static char inet_buf[INET6_ADDRSTRLEN];
+
+ if (getnameinfo(saddr, saddr->sa_len, inet_buf, sizeof(inet_buf),
+ NULL, 0, NI_NUMERICHOST) == 0)
+ return inet_buf;
+ return "???";
+}
+
+/* sm_check_hostname -------------------------------------------------------- */
+/*
+ * Purpose: Check `mon_name' member of sm_name struct to ensure that the array
+ * consists only of printable characters.
+ *
+ * Returns: TRUE if hostname is good. FALSE if hostname contains binary or
+ * otherwise non-printable characters.
+ *
+ * Notes: Will syslog(3) to warn of corrupt hostname.
+ */
+
+int sm_check_hostname(struct svc_req *req, char *arg)
+{
+ int len, dstlen, ret;
+ struct sockaddr *claddr;
+ char *dst;
+
+ len = strlen(arg);
+ dstlen = (4 * len) + 1;
+ dst = malloc(dstlen);
+ claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ;
+ ret = 1;
+
+ if (claddr == NULL || dst == NULL)
+ {
+ ret = 0;
+ }
+ else if (strvis(dst, arg, VIS_WHITE) != len)
+ {
+ syslog(LOG_ERR,
+ "sm_stat: client %s hostname %s contained invalid characters.",
+ from_addr(claddr),
+ dst);
+ ret = 0;
+ }
+ free(dst);
+ return (ret);
+}
+
+/* sm_stat_1 --------------------------------------------------------------- */
+/*
+ Purpose: RPC call to enquire if a host can be monitored
+ Returns: TRUE for any hostname that can be looked up to give
+ an address.
+*/
+
+struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req)
+{
+ static sm_stat_res res;
+ struct addrinfo *ai;
+ struct sockaddr *claddr;
+ static int err;
+
+ err = 1;
+ if ((err = sm_check_hostname(req, arg->mon_name)) == 0)
+ {
+ res.res_stat = stat_fail;
+ }
+ if (err != 0)
+ {
+ if (debug)
+ syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
+ if (getaddrinfo(arg->mon_name, NULL, NULL, &ai) == 0) {
+ res.res_stat = stat_succ;
+ freeaddrinfo(ai);
+ }
+ else
+ {
+ claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ;
+ syslog(LOG_ERR, "invalid hostname to sm_stat from %s: %s",
+ from_addr(claddr), arg->mon_name);
+ res.res_stat = stat_fail;
+ }
+ }
+ res.state = status_info->ourState;
+ return (&res);
+}
+
+/* sm_mon_1 ---------------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to establish a monitor request
+ Returns: Success, unless lack of resources prevents
+ the necessary structures from being set up
+ to record the request, or if the hostname is not
+ valid (as judged by getaddrinfo())
+*/
+
+struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
+{
+ static sm_stat_res res;
+ HostInfo *hp;
+ static int err;
+ MonList *lp;
+ struct addrinfo *ai;
+
+ if ((err = sm_check_hostname(req, arg->mon_id.mon_name)) == 0)
+ {
+ res.res_stat = stat_fail;
+ }
+
+ if (err != 0)
+ {
+ if (debug)
+ {
+ syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
+ syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
+ arg->mon_id.my_id.my_name,
+ arg->mon_id.my_id.my_prog,
+ arg->mon_id.my_id.my_vers,
+ arg->mon_id.my_id.my_proc);
+ }
+ res.res_stat = stat_fail; /* Assume fail until set otherwise */
+ res.state = status_info->ourState;
+
+ /* Find existing host entry, or create one if not found */
+ /* If find_host() fails, it will have logged the error already. */
+ if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0)
+ {
+ syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
+ return (&res);
+ }
+ freeaddrinfo(ai);
+ if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
+ {
+ lp = (MonList *)malloc(sizeof(MonList));
+ if (!lp)
+ {
+ syslog(LOG_ERR, "Out of memory");
+ }
+ else
+ {
+ strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
+ lp->notifyProg = arg->mon_id.my_id.my_prog;
+ lp->notifyVers = arg->mon_id.my_id.my_vers;
+ lp->notifyProc = arg->mon_id.my_id.my_proc;
+ memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
+
+ lp->next = hp->monList;
+ hp->monList = lp;
+ sync_file();
+
+ res.res_stat = stat_succ; /* Report success */
+ }
+ }
+ }
+ return (&res);
+}
+
+/* do_unmon ---------------------------------------------------------------- */
+/*
+ Purpose: Remove a monitor request from a host
+ Returns: TRUE if found, FALSE if not found.
+ Notes: Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
+ In the unlikely event of more than one identical monitor
+ request, all are removed.
+*/
+
+static int do_unmon(HostInfo *hp, my_id *idp)
+{
+ MonList *lp, *next;
+ MonList *last = NULL;
+ int result = FALSE;
+
+ lp = hp->monList;
+ while (lp)
+ {
+ if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
+ && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
+ && (idp->my_vers == lp->notifyVers))
+ {
+ /* found one. Unhook from chain and free. */
+ next = lp->next;
+ if (last) last->next = next;
+ else hp->monList = next;
+ free(lp);
+ lp = next;
+ result = TRUE;
+ }
+ else
+ {
+ last = lp;
+ lp = lp->next;
+ }
+ }
+ return (result);
+}
+
+/* sm_unmon_1 -------------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to release a monitor request.
+ Returns: Local machine's status number
+ Notes: The supplied mon_id should match the value passed in an
+ earlier call to sm_mon_1
+*/
+
+struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req __unused)
+{
+ static sm_stat res;
+ HostInfo *hp;
+
+ if (debug)
+ {
+ syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
+ syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
+ arg->mon_name,
+ arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc);
+ }
+
+ if ((hp = find_host(arg->mon_name, FALSE)))
+ {
+ if (do_unmon(hp, &arg->my_id)) sync_file();
+ else
+ {
+ syslog(LOG_ERR, "unmon request from %s, no matching monitor",
+ arg->my_id.my_name);
+ }
+ }
+ else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
+ arg->my_id.my_name, arg->mon_name);
+
+ res.state = status_info->ourState;
+
+ return (&res);
+}
+
+/* sm_unmon_all_1 ---------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to release monitor requests.
+ Returns: Local machine's status number
+ Notes: Releases all monitor requests (if any) from the specified
+ host and program number.
+*/
+
+struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req __unused)
+{
+ static sm_stat res;
+ HostInfo *hp;
+ int i;
+
+ if (debug)
+ {
+ syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
+ arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
+ }
+
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
+ {
+ do_unmon(hp, arg);
+ }
+ sync_file();
+
+ res.state = status_info->ourState;
+
+ return (&res);
+}
+
+/* sm_simu_crash_1 --------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to simulate a crash
+ Returns: Nothing
+ Notes: Standardised mechanism for debug purposes
+ The specification says that we should drop all of our
+ status information (apart from the list of monitored hosts
+ on disc). However, this would confuse the rpc.lockd
+ which would be unaware that all of its monitor requests
+ had been silently junked. Hence we in fact retain all
+ current requests and simply increment the status counter
+ and inform all hosts on the monitor list.
+*/
+
+void *sm_simu_crash_1_svc(void *v __unused, struct svc_req *req __unused)
+{
+ static char dummy;
+ int work_to_do;
+ HostInfo *hp;
+ int i;
+
+ work_to_do = FALSE;
+ if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
+
+ /* Simulate crash by setting notify-required flag on all monitored */
+ /* hosts, and incrementing our status number. notify_hosts() is */
+ /* then called to fork a process to do the notifications. */
+
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
+ {
+ if (hp->monList)
+ {
+ work_to_do = TRUE;
+ hp->notifyReqd = TRUE;
+ }
+ }
+ status_info->ourState += 2; /* always even numbers if not crashed */
+
+ if (work_to_do) notify_hosts();
+
+ return (&dummy);
+}
+
+/* sm_notify_1 ------------------------------------------------------------- */
+/*
+ Purpose: RPC procedure notifying local statd of the crash of another
+ Returns: Nothing
+ Notes: There is danger of deadlock, since it is quite likely that
+ the client procedure that we call will in turn call us
+ to remove or adjust the monitor request.
+ We therefore fork() a process to do the notifications.
+ Note that the main HostInfo structure is in a mmap()
+ region and so will be shared with the child, but the
+ monList pointed to by the HostInfo is in normal memory.
+ Hence if we read the monList before forking, we are
+ protected from the parent servicing other requests
+ that modify the list.
+*/
+
+void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req __unused)
+{
+ struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
+ CLIENT *cli;
+ static char dummy;
+ sm_status tx_arg; /* arg sent to callback procedure */
+ MonList *lp;
+ HostInfo *hp;
+ pid_t pid;
+
+ if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
+ arg->mon_name, arg->state);
+
+ hp = find_host(arg->mon_name, FALSE);
+ if (!hp)
+ {
+ /* Never heard of this host - why is it notifying us? */
+ syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
+ return (&dummy);
+ }
+ lp = hp->monList;
+ if (!lp) return (&dummy); /* We know this host, but have no */
+ /* outstanding requests. */
+ pid = fork();
+ if (pid == -1)
+ {
+ syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
+ return (NULL); /* no answer, the client will retry */
+ }
+ if (pid) return (&dummy); /* Parent returns */
+
+ while (lp)
+ {
+ tx_arg.mon_name = arg->mon_name;
+ tx_arg.state = arg->state;
+ memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
+ cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
+ if (!cli)
+ {
+ syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
+ clnt_spcreateerror(""));
+ }
+ else
+ {
+ if (clnt_call(cli, lp->notifyProc, (xdrproc_t)xdr_sm_status, &tx_arg,
+ (xdrproc_t)xdr_void, &dummy, timeout) != RPC_SUCCESS)
+ {
+ syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
+ lp->notifyHost);
+ }
+ clnt_destroy(cli);
+ }
+ lp = lp->next;
+ }
+
+ exit (0); /* Child quits */
+}
diff --git a/usr.sbin/rpc.statd/rpc.statd.8 b/usr.sbin/rpc.statd/rpc.statd.8
new file mode 100644
index 0000000..32559ad
--- /dev/null
+++ b/usr.sbin/rpc.statd/rpc.statd.8
@@ -0,0 +1,137 @@
+.\" -*- nroff -*-
+.\"
+.\" 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. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 1, 2007
+.Dt RPC.STATD 8
+.Os
+.Sh NAME
+.Nm rpc.statd
+.Nd host status monitoring daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl h Ar bindip
+.Op Fl p Ar port
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is a daemon which co-operates with
+.Nm
+daemons on other hosts to provide
+a status monitoring service.
+The daemon accepts requests from
+programs running on the local host (typically,
+.Xr rpc.lockd 8 ,
+the NFS file locking daemon) to monitor the status of specified
+hosts.
+If a monitored host crashes and restarts, the remote daemon will
+notify the local daemon, which in turn will notify the local program(s)
+which requested the monitoring service.
+Conversely, if this host crashes
+and re-starts, when the
+.Nm
+re-starts, it will notify all of the hosts which were being monitored
+at the time of the crash.
+.Pp
+The following option is available:
+.Bl -tag -width indent
+.It Fl d
+Cause debugging information to be written to syslog, recording
+all RPC transactions to the daemon.
+These messages are logged with level
+LOG_DEBUG and facility LOG_DAEMON.
+Error conditions are logged irrespective
+of this option, using level LOG_ERR.
+.It Fl h Ar bindip
+Specify specific IP addresses to bind to.
+This option may be specified multiple times.
+If no
+.Fl h
+option is specified,
+.Nm
+will bind to
+.Dv INADDR_ANY .
+Note that when specifying IP addresses with
+.Fl h ,
+.Nm
+will automatically add
+.Li 127.0.0.1
+and if IPv6 is enabled,
+.Li ::1
+to the list.
+.It Fl p
+The
+.Fl p
+option allow to force the daemon to bind to the specified
+.Ar port ,
+for both AF_INET and AF_INET6 address families.
+.El
+.Pp
+The
+.Nm
+utility must NOT be invoked by
+.Xr inetd 8
+because the protocol assumes that the daemon will run from system start time.
+Instead, it should be run from
+.Xr rc 8
+after the network has been started.
+.Sh FILES
+.Bl -tag -width /usr/include/rpcsvc/sm_inter.x -compact
+.It Pa /var/db/statd.status
+non-volatile record of currently monitored hosts.
+.It Pa /usr/include/rpcsvc/sm_inter.x
+RPC protocol specification used by local applications to register monitoring requests.
+.El
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr rc 8 ,
+.Xr rpc.lockd 8
+.Sh STANDARDS
+The implementation is based on the specification in X/Open CAE Specification
+C218, "Protocols for X/Open PC Interworking: XNFS, Issue 4", ISBN 1 872630 66 9
+.Sh BUGS
+There is no means for the daemon to tell when a monitored host has
+disappeared permanently (e.g.\& catastrophic hardware failure), as opposed
+to transient failure of the host or an intermediate router.
+At present,
+it will re-try notification attempts at frequent intervals for 10 minutes,
+then hourly, and finally gives up after 24 hours.
+.Pp
+The protocol requires that symmetric monitor requests are made to both
+the local and remote daemon in order to establish a monitored relationship.
+This is convenient for the NFS locking protocol, but probably reduces the
+usefulness of the monitoring system for other applications.
+.Pp
+The current implementation uses more than 1Kbyte per monitored host in
+the status file (and also in VM).
+This may be inefficient for NFS servers
+with large numbers of clients.
diff --git a/usr.sbin/rpc.statd/statd.c b/usr.sbin/rpc.statd/statd.c
new file mode 100644
index 0000000..256f58d
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.c
@@ -0,0 +1,655 @@
+/*
+ * 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.
+ *
+ */
+
+/* main() function for status monitor daemon. Some of the code in this */
+/* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x */
+/* The actual program logic is in the file procs.c */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <signal.h>
+#include <unistd.h>
+#include "statd.h"
+
+#define GETPORT_MAXTRY 20 /* Max tries to get a port # */
+
+int debug = 0; /* Controls syslog() calls for debug messages */
+
+char **hosts, *svcport_str = NULL;
+int nhosts = 0;
+int xcreated = 0;
+static int mallocd_svcport = 0;
+static int *sock_fd;
+static int sock_fdcnt;
+static int sock_fdpos;
+
+static int create_service(struct netconfig *nconf);
+static void complete_service(struct netconfig *nconf, char *port_str);
+static void clearout_service(void);
+static void handle_sigchld(int sig);
+void out_of_mem(void);
+
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction sa;
+ struct netconfig *nconf;
+ void *nc_handle;
+ in_port_t svcport;
+ int ch, i, s;
+ char *endptr, **hosts_bak;
+ int have_v6 = 1;
+ int maxrec = RPC_MAXDATASIZE;
+ int attempt_cnt, port_len, port_pos, ret;
+ char **port_list;
+
+ while ((ch = getopt(argc, argv, "dh:p:")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'h':
+ ++nhosts;
+ hosts_bak = hosts;
+ hosts_bak = realloc(hosts, nhosts * sizeof(char *));
+ if (hosts_bak == NULL) {
+ if (hosts != NULL) {
+ for (i = 0; i < nhosts; i++)
+ free(hosts[i]);
+ free(hosts);
+ out_of_mem();
+ }
+ }
+ hosts = hosts_bak;
+ hosts[nhosts - 1] = strdup(optarg);
+ if (hosts[nhosts - 1] == NULL) {
+ for (i = 0; i < (nhosts - 1); i++)
+ free(hosts[i]);
+ free(hosts);
+ out_of_mem();
+ }
+ break;
+ case 'p':
+ endptr = NULL;
+ svcport = (in_port_t)strtoul(optarg, &endptr, 10);
+ if (endptr == NULL || *endptr != '\0' || svcport == 0 ||
+ svcport >= IPPORT_MAX)
+ usage();
+
+ svcport_str = strdup(optarg);
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ (void)rpcb_unset(SM_PROG, SM_VERS, NULL);
+
+ /*
+ * Check if IPv6 support is present.
+ */
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s < 0)
+ have_v6 = 0;
+ else
+ close(s);
+
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ /*
+ * If no hosts were specified, add a wildcard entry to bind to
+ * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
+ * list.
+ */
+ if (nhosts == 0) {
+ hosts = malloc(sizeof(char *));
+ if (hosts == NULL)
+ out_of_mem();
+
+ hosts[0] = "*";
+ nhosts = 1;
+ } else {
+ hosts_bak = hosts;
+ if (have_v6) {
+ hosts_bak = realloc(hosts, (nhosts + 2) *
+ sizeof(char *));
+ if (hosts_bak == NULL) {
+ for (i = 0; i < nhosts; i++)
+ free(hosts[i]);
+ free(hosts);
+ out_of_mem();
+ } else
+ hosts = hosts_bak;
+
+ nhosts += 2;
+ hosts[nhosts - 2] = "::1";
+ } else {
+ hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
+ if (hosts_bak == NULL) {
+ for (i = 0; i < nhosts; i++)
+ free(hosts[i]);
+
+ free(hosts);
+ out_of_mem();
+ } else {
+ nhosts += 1;
+ hosts = hosts_bak;
+ }
+ }
+ hosts[nhosts - 1] = "127.0.0.1";
+ }
+
+ attempt_cnt = 1;
+ sock_fdcnt = 0;
+ sock_fd = NULL;
+ port_list = NULL;
+ port_len = 0;
+ nc_handle = setnetconfig();
+ while ((nconf = getnetconfig(nc_handle))) {
+ /* We want to listen only on udp6, tcp6, udp, tcp transports */
+ if (nconf->nc_flag & NC_VISIBLE) {
+ /* Skip if there's no IPv6 support */
+ if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
+ /* DO NOTHING */
+ } else {
+ ret = create_service(nconf);
+ if (ret == 1)
+ /* Ignore this call */
+ continue;
+ if (ret < 0) {
+ /*
+ * Failed to bind port, so close off
+ * all sockets created and try again
+ * if the port# was dynamically
+ * assigned via bind(2).
+ */
+ clearout_service();
+ if (mallocd_svcport != 0 &&
+ attempt_cnt < GETPORT_MAXTRY) {
+ free(svcport_str);
+ svcport_str = NULL;
+ mallocd_svcport = 0;
+ } else {
+ errno = EADDRINUSE;
+ syslog(LOG_ERR,
+ "bindresvport_sa: %m");
+ exit(1);
+ }
+
+ /* Start over at the first service. */
+ free(sock_fd);
+ sock_fdcnt = 0;
+ sock_fd = NULL;
+ nc_handle = setnetconfig();
+ attempt_cnt++;
+ } else if (mallocd_svcport != 0 &&
+ attempt_cnt == GETPORT_MAXTRY) {
+ /*
+ * For the last attempt, allow
+ * different port #s for each nconf
+ * by saving the svcport_str and
+ * setting it back to NULL.
+ */
+ port_list = realloc(port_list,
+ (port_len + 1) * sizeof(char *));
+ if (port_list == NULL)
+ out_of_mem();
+ port_list[port_len++] = svcport_str;
+ svcport_str = NULL;
+ mallocd_svcport = 0;
+ }
+ }
+ }
+ }
+
+ /*
+ * Successfully bound the ports, so call complete_service() to
+ * do the rest of the setup on the service(s).
+ */
+ sock_fdpos = 0;
+ port_pos = 0;
+ nc_handle = setnetconfig();
+ while ((nconf = getnetconfig(nc_handle))) {
+ /* We want to listen only on udp6, tcp6, udp, tcp transports */
+ if (nconf->nc_flag & NC_VISIBLE) {
+ /* Skip if there's no IPv6 support */
+ if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
+ /* DO NOTHING */
+ } else if (port_list != NULL) {
+ if (port_pos >= port_len) {
+ syslog(LOG_ERR, "too many port#s");
+ exit(1);
+ }
+ complete_service(nconf, port_list[port_pos++]);
+ } else
+ complete_service(nconf, svcport_str);
+ }
+ }
+ endnetconfig(nc_handle);
+ free(sock_fd);
+ if (port_list != NULL) {
+ for (port_pos = 0; port_pos < port_len; port_pos++)
+ free(port_list[port_pos]);
+ free(port_list);
+ }
+
+ init_file("/var/db/statd.status");
+
+ /* Note that it is NOT sensible to run this program from inetd - the */
+ /* protocol assumes that it will run immediately at boot time. */
+ daemon(0, 0);
+ openlog("rpc.statd", 0, LOG_DAEMON);
+ if (debug) syslog(LOG_INFO, "Starting - debug enabled");
+ else syslog(LOG_INFO, "Starting");
+
+ /* 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);
+
+ /* Initialisation now complete - start operating */
+ notify_hosts(); /* Forks a process (if necessary) to do the */
+ /* SM_NOTIFY calls, which may be slow. */
+
+ svc_run(); /* Should never return */
+ exit(1);
+}
+
+/*
+ * This routine creates and binds sockets on the appropriate
+ * addresses. It gets called one time for each transport.
+ * It returns 0 upon success, 1 for ingore the call and -1 to indicate
+ * bind failed with EADDRINUSE.
+ * Any file descriptors that have been created are stored in sock_fd and
+ * the total count of them is maintained in sock_fdcnt.
+ */
+static int
+create_service(struct netconfig *nconf)
+{
+ struct addrinfo hints, *res = NULL;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct __rpc_sockinfo si;
+ int aicode;
+ int fd;
+ int nhostsbak;
+ int r;
+ u_int32_t host_addr[4]; /* IPv4 or IPv6 */
+ int mallocd_res;
+
+ if ((nconf->nc_semantics != NC_TPI_CLTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ return (1); /* not my type */
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if (!__rpc_nconf2sockinfo(nconf, &si)) {
+ syslog(LOG_ERR, "cannot get information for %s",
+ nconf->nc_netid);
+ return (1);
+ }
+
+ /* Get rpc.statd's address on this transport */
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+
+ /*
+ * Bind to specific IPs if asked to
+ */
+ nhostsbak = nhosts;
+ while (nhostsbak > 0) {
+ --nhostsbak;
+ sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
+ if (sock_fd == NULL)
+ out_of_mem();
+ sock_fd[sock_fdcnt++] = -1; /* Set invalid for now. */
+ mallocd_res = 0;
+ hints.ai_flags = AI_PASSIVE;
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if ((fd = __rpc_nconf2fd(nconf)) < 0) {
+ syslog(LOG_ERR, "cannot create socket for %s",
+ nconf->nc_netid);
+ continue;
+ }
+ switch (hints.ai_family) {
+ case AF_INET:
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags |= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET6 address.
+ */
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+ break;
+ case AF_INET6:
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags |= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET address.
+ */
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If no hosts were specified, just bind to INADDR_ANY
+ */
+ if (strcmp("*", hosts[nhostsbak]) == 0) {
+ if (svcport_str == NULL) {
+ res = malloc(sizeof(struct addrinfo));
+ if (res == NULL)
+ out_of_mem();
+ mallocd_res = 1;
+ res->ai_flags = hints.ai_flags;
+ res->ai_family = hints.ai_family;
+ res->ai_protocol = hints.ai_protocol;
+ switch (res->ai_family) {
+ case AF_INET:
+ sin = malloc(sizeof(struct sockaddr_in));
+ if (sin == NULL)
+ out_of_mem();
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(0);
+ sin->sin_addr.s_addr = htonl(INADDR_ANY);
+ res->ai_addr = (struct sockaddr*) sin;
+ res->ai_addrlen = (socklen_t)
+ sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ sin6 = malloc(sizeof(struct sockaddr_in6));
+ if (sin6 == NULL)
+ out_of_mem();
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(0);
+ sin6->sin6_addr = in6addr_any;
+ res->ai_addr = (struct sockaddr*) sin6;
+ res->ai_addrlen = (socklen_t)
+ sizeof(struct sockaddr_in6);
+ break;
+ default:
+ syslog(LOG_ERR, "bad addr fam %d",
+ res->ai_family);
+ exit(1);
+ }
+ } else {
+ if ((aicode = getaddrinfo(NULL, svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid,
+ gai_strerror(aicode));
+ close(fd);
+ continue;
+ }
+ }
+ } else {
+ if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid, gai_strerror(aicode));
+ close(fd);
+ continue;
+ }
+ }
+
+ /* Store the fd. */
+ sock_fd[sock_fdcnt - 1] = fd;
+
+ /* Now, attempt the bind. */
+ r = bindresvport_sa(fd, res->ai_addr);
+ if (r != 0) {
+ if (errno == EADDRINUSE && mallocd_svcport != 0) {
+ if (mallocd_res != 0) {
+ free(res->ai_addr);
+ free(res);
+ } else
+ freeaddrinfo(res);
+ return (-1);
+ }
+ syslog(LOG_ERR, "bindresvport_sa: %m");
+ exit(1);
+ }
+
+ if (svcport_str == NULL) {
+ svcport_str = malloc(NI_MAXSERV * sizeof(char));
+ if (svcport_str == NULL)
+ out_of_mem();
+ mallocd_svcport = 1;
+
+ if (getnameinfo(res->ai_addr,
+ res->ai_addr->sa_len, NULL, NI_MAXHOST,
+ svcport_str, NI_MAXSERV * sizeof(char),
+ NI_NUMERICHOST | NI_NUMERICSERV))
+ errx(1, "Cannot get port number");
+ }
+ if (mallocd_res != 0) {
+ free(res->ai_addr);
+ free(res);
+ } else
+ freeaddrinfo(res);
+ res = NULL;
+ }
+ return (0);
+}
+
+/*
+ * Called after all the create_service() calls have succeeded, to complete
+ * the setup and registration.
+ */
+static void
+complete_service(struct netconfig *nconf, char *port_str)
+{
+ struct addrinfo hints, *res = NULL;
+ struct __rpc_sockinfo si;
+ struct netbuf servaddr;
+ SVCXPRT *transp = NULL;
+ int aicode, fd, nhostsbak;
+ int registered = 0;
+
+ if ((nconf->nc_semantics != NC_TPI_CLTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ return; /* not my type */
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if (!__rpc_nconf2sockinfo(nconf, &si)) {
+ syslog(LOG_ERR, "cannot get information for %s",
+ nconf->nc_netid);
+ return;
+ }
+
+ nhostsbak = nhosts;
+ while (nhostsbak > 0) {
+ --nhostsbak;
+ if (sock_fdpos >= sock_fdcnt) {
+ /* Should never happen. */
+ syslog(LOG_ERR, "Ran out of socket fd's");
+ return;
+ }
+ fd = sock_fd[sock_fdpos++];
+ if (fd < 0)
+ continue;
+
+ if (nconf->nc_semantics != NC_TPI_CLTS)
+ listen(fd, SOMAXCONN);
+
+ transp = svc_tli_create(fd, nconf, NULL,
+ RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+
+ if (transp != (SVCXPRT *) NULL) {
+ if (!svc_register(transp, SM_PROG, SM_VERS,
+ sm_prog_1, 0)) {
+ syslog(LOG_ERR, "can't register on %s",
+ nconf->nc_netid);
+ } else {
+ if (!svc_reg(transp, SM_PROG, SM_VERS,
+ sm_prog_1, NULL))
+ syslog(LOG_ERR,
+ "can't register %s SM_PROG service",
+ nconf->nc_netid);
+ }
+ } else
+ syslog(LOG_WARNING, "can't create %s services",
+ nconf->nc_netid);
+
+ if (registered == 0) {
+ registered = 1;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+
+
+ if ((aicode = getaddrinfo(NULL, port_str, &hints,
+ &res)) != 0) {
+ syslog(LOG_ERR, "cannot get local address: %s",
+ gai_strerror(aicode));
+ exit(1);
+ }
+
+ servaddr.buf = malloc(res->ai_addrlen);
+ memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
+ servaddr.len = res->ai_addrlen;
+
+ rpcb_set(SM_PROG, SM_VERS, nconf, &servaddr);
+
+ xcreated++;
+ freeaddrinfo(res);
+ }
+ } /* end while */
+}
+
+/*
+ * Clear out sockets after a failure to bind one of them, so that the
+ * cycle of socket creation/binding can start anew.
+ */
+static void
+clearout_service(void)
+{
+ int i;
+
+ for (i = 0; i < sock_fdcnt; i++) {
+ if (sock_fd[i] >= 0) {
+ shutdown(sock_fd[i], SHUT_RDWR);
+ close(sock_fd[i]);
+ }
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rpc.statd [-d] [-h <bindip>] [-p <port>]\n");
+ exit(1);
+}
+
+/* handle_sigchld ---------------------------------------------------------- */
+/*
+ Purpose: Catch SIGCHLD and collect process status
+ Retruns: Nothing.
+ Notes: No special action required, other than to collect the
+ process status and hence allow the child to die:
+ we only use child processes for asynchronous transmission
+ of SM_NOTIFY to other systems, so it is normal for the
+ children to exit when they have done their work.
+*/
+
+static void handle_sigchld(int sig __unused)
+{
+ int pid, status;
+ pid = wait4(-1, &status, WNOHANG, (struct rusage*)0);
+ if (!pid) syslog(LOG_ERR, "Phantom SIGCHLD??");
+ else if (status == 0)
+ {
+ if (debug) syslog(LOG_DEBUG, "Child %d exited OK", pid);
+ }
+ else syslog(LOG_ERR, "Child %d failed with status %d", pid,
+ WEXITSTATUS(status));
+}
+
+/*
+ * Out of memory, fatal
+ */
+void
+out_of_mem()
+{
+
+ syslog(LOG_ERR, "out of memory");
+ exit(2);
+}
diff --git a/usr.sbin/rpc.statd/statd.h b/usr.sbin/rpc.statd/statd.h
new file mode 100644
index 0000000..a82d760
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+
+
+#include "sm_inter.h"
+
+/* ------------------------------------------------------------------------- */
+/*
+ Data structures for recording monitored hosts
+
+ The information held by the status monitor comprises a list of hosts
+ that we have been asked to monitor, and, associated with each monitored
+ host, one or more clients to be called back if the monitored host crashes.
+
+ The list of monitored hosts must be retained over a crash, so that upon
+ re-boot we can call the SM_NOTIFY procedure in all those hosts so as to
+ cause them to start recovery processing. On the other hand, the client
+ call-backs are not required to be preserved: they are assumed (in the
+ protocol design) to be local processes which will have crashed when
+ we did, and so are discarded on restart.
+
+ We handle this by keeping the list of monitored hosts in a file
+ (/var/statd.state) which is mmap()ed and whose format is described
+ by the typedef FileLayout. The lists of client callbacks are chained
+ off this structure, but are held in normal memory and so will be
+ lost after a re-boot. Hence the actual values of MonList * pointers
+ in the copy on disc have no significance, but their NULL/non-NULL
+ status indicates whether this host is actually being monitored or if it
+ is an empty slot in the file.
+*/
+
+typedef struct MonList_s
+{
+ struct MonList_s *next; /* Next in list or NULL */
+ char notifyHost[SM_MAXSTRLEN + 1]; /* Host to notify */
+ int notifyProg; /* RPC program number to call */
+ int notifyVers; /* version number */
+ int notifyProc; /* procedure number */
+ unsigned char notifyData[16]; /* Opaque data from caller */
+} MonList;
+
+typedef struct
+{
+ char hostname[SM_MAXSTRLEN + 1]; /* Name of monitored host */
+ int notifyReqd; /* TRUE if we've crashed and not yet */
+ /* informed the monitored host */
+ MonList *monList; /* List of clients to inform if we */
+ /* hear that the monitored host has */
+ /* crashed, NULL if no longer monitored */
+} HostInfo;
+
+
+/* Overall file layout. */
+
+typedef struct
+{
+ int ourState; /* State number as defined in statd protocol */
+ int noOfHosts; /* Number of elements in hosts[] */
+ char reserved[248]; /* Reserved for future use */
+ HostInfo hosts[1]; /* vector of monitored hosts */
+} FileLayout;
+
+#define HEADER_LEN (sizeof(FileLayout) - sizeof(HostInfo))
+
+/* ------------------------------------------------------------------------- */
+
+/* Global variables */
+
+extern FileLayout *status_info; /* The mmap()ed status file */
+
+extern int debug; /* =1 to enable diagnostics to syslog */
+
+/* Function prototypes */
+
+extern HostInfo *find_host(char * /*hostname*/, int /*create*/);
+extern void init_file(const char * /*filename*/);
+extern void notify_hosts(void);
+extern void sync_file(void);
+extern int sm_check_hostname(struct svc_req *req, char *arg);
diff --git a/usr.sbin/rpc.statd/test.c b/usr.sbin/rpc.statd/test.c
new file mode 100644
index 0000000..6df1501
--- /dev/null
+++ b/usr.sbin/rpc.statd/test.c
@@ -0,0 +1,144 @@
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/sm_inter.h>
+
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+struct sm_stat_res *
+sm_stat_1(argp, clnt)
+ struct sm_name *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_STAT, xdr_sm_name, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat_res *
+sm_mon_1(argp, clnt)
+ struct mon *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_MON, xdr_mon, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat *
+sm_unmon_1(argp, clnt)
+ struct mon_id *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_UNMON, xdr_mon_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat *
+sm_unmon_all_1(argp, clnt)
+ struct my_id *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_UNMON_ALL, xdr_my_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+sm_simu_crash_1(argp, clnt)
+ void *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_SIMU_CRASH, xdr_void, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+int main(int argc, char **argv)
+{
+ CLIENT *cli;
+ char dummy;
+ void *out;
+ struct mon mon;
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "usage: test <hostname> | crash\n");
+ fprintf(stderr, "always talks to statd at localhost\n");
+ exit(1);
+ }
+
+ printf("Creating client for localhost\n" );
+ cli = clnt_create("localhost", SM_PROG, SM_VERS, "udp");
+ if (!cli)
+ {
+ printf("Failed to create client\n");
+ exit(1);
+ }
+
+ mon.mon_id.mon_name = argv[1];
+ mon.mon_id.my_id.my_name = argv[1];
+ mon.mon_id.my_id.my_prog = SM_PROG;
+ mon.mon_id.my_id.my_vers = SM_VERS;
+ mon.mon_id.my_id.my_proc = 1; /* have it call sm_stat() !!! */
+
+ if (strcmp(argv[1], "crash"))
+ {
+ /* Hostname given */
+ struct sm_stat_res *res;
+ if (res = sm_mon_1(&mon, cli))
+ {
+ printf("Success!\n");
+ }
+ else
+ {
+ printf("Fail\n");
+ }
+ }
+ else
+ {
+ if (out = sm_simu_crash_1(&dummy, cli))
+ {
+ printf("Success!\n");
+ }
+ else
+ {
+ printf("Fail\n");
+ }
+ }
+
+ return 0;
+}
OpenPOWER on IntegriCloud