summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorNikolay Sturm <sturm@cvs.openbsd.org>2008-06-15 04:43:29 +0000
committerNikolay Sturm <sturm@cvs.openbsd.org>2008-06-15 04:43:29 +0000
commit84c25807abc987ed60252eb4ec4aebbf6ae15ac0 (patch)
treebab917fd0e33e204cbaa9f69ff6f037e9e5f2d18 /usr.sbin
parente25055c0752ac5483c82035ebf36e0c1bfd07279 (diff)
initial import of rpc.statd, basically from NetBSD
"just get it in" deraadt
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/rpc.statd/Makefile26
-rw-r--r--usr.sbin/rpc.statd/procs.c345
-rw-r--r--usr.sbin/rpc.statd/rpc.statd.8113
-rw-r--r--usr.sbin/rpc.statd/statd.c597
-rw-r--r--usr.sbin/rpc.statd/statd.h123
-rw-r--r--usr.sbin/rpc.statd/test.c126
6 files changed, 1330 insertions, 0 deletions
diff --git a/usr.sbin/rpc.statd/Makefile b/usr.sbin/rpc.statd/Makefile
new file mode 100644
index 00000000000..05b4f83e7e1
--- /dev/null
+++ b/usr.sbin/rpc.statd/Makefile
@@ -0,0 +1,26 @@
+# $OpenBSD: Makefile,v 1.1 2008/06/15 04:43:28 sturm Exp $
+
+PROG= rpc.statd
+SRCS= procs.c sm_inter_svc.c statd.c
+MAN= rpc.statd.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CFLAGS+= -I.
+
+CLEANFILES= sm_inter_svc.c sm_inter.h
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/sm_inter.x
+RPCGEN= rpcgen -L -C
+
+sm_inter_svc.c: ${RPCSRC} sm_inter.h
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+sm_inter.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+test: ${.CURDIR}/test.c
+ cc -o test ${.CURDIR}/test.c -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.statd/procs.c b/usr.sbin/rpc.statd/procs.c
new file mode 100644
index 00000000000..285a7b88afb
--- /dev/null
+++ b/usr.sbin/rpc.statd/procs.c
@@ -0,0 +1,345 @@
+/* $OpenBSD: procs.c,v 1.1 2008/06/15 04:43:28 sturm 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 <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+
+#include "statd.h"
+
+/* 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;
+
+ NO_ALARM;
+ if (debug)
+ syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
+
+ if (gethostbyname(arg->mon_name))
+ res.res_stat = stat_succ;
+ else {
+ syslog(LOG_ERR, "invalid hostname to sm_stat: %s",
+ arg->mon_name);
+ res.res_stat = stat_fail;
+ }
+
+ res.state = status_info.ourState;
+ ALARM;
+ 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 gethostbyname())
+ */
+struct sm_stat_res *
+sm_mon_1_svc(mon *arg, struct svc_req *req)
+{
+ static sm_stat_res res;
+ HostInfo *hp, h;
+ MonList *lp;
+
+ NO_ALARM;
+ 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 (!gethostbyname(arg->mon_id.mon_name)) {
+ syslog(LOG_ERR, "Invalid hostname to sm_mon: %s",
+ arg->mon_id.mon_name);
+ return &res;
+ }
+
+ if ((hp = find_host(arg->mon_id.mon_name, &h)) == NULL)
+ memset(hp = &h, 0, sizeof(h));
+
+ 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;
+ change_host(arg->mon_id.mon_name, hp);
+ sync_file();
+ res.res_stat = stat_succ; /* Report success */
+ }
+ ALARM;
+ 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.
+ */
+int
+do_unmon(char *name, HostInfo *hp, void *ptr)
+{
+ my_id *idp = ptr;
+ 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)
+{
+ static sm_stat res;
+ HostInfo *hp, h;
+
+ NO_ALARM;
+ 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->my_id.my_name, arg->my_id.my_prog,
+ arg->my_id.my_vers, arg->my_id.my_proc);
+ }
+ if ((hp = find_host(arg->mon_name, &h)) != NULL) {
+ if (do_unmon(arg->mon_name, hp, &arg->my_id)) {
+ change_host(arg->mon_name, hp);
+ 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;
+ ALARM;
+
+ 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)
+{
+ static sm_stat res;
+
+ NO_ALARM;
+ 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);
+ }
+
+ unmon_hosts();
+ sync_file();
+
+ res.state = status_info.ourState;
+ ALARM;
+
+ 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, struct svc_req *req)
+{
+ static char dummy;
+
+ NO_ALARM;
+ if (debug)
+ syslog(LOG_DEBUG, "simu_crash called!!");
+
+ reset_database();
+ ALARM;
+ notify_handler(0);
+
+ 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)
+{
+ struct timeval timeout = {20, 0}; /* 20 secs timeout */
+ CLIENT *cli;
+ static char dummy;
+ status tx_arg; /* arg sent to callback procedure */
+ MonList *lp;
+ HostInfo *hp, h;
+ 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, &h);
+ if (!hp) {
+ /* Never heard of this host - why is it notifying us? */
+ syslog(LOG_DEBUG, "Unsolicited notification from host %s",
+ arg->mon_name);
+ return (&dummy);
+ }
+ lp = hp->monList;
+ if (!lp) /* We know this host, but have no outstanding requests. */
+ return (&dummy);
+
+ sync_file();
+ pid = fork();
+ if (pid == -1) {
+ syslog(LOG_ERR, "Unable to fork notify process - %s",
+ strerror(errno));
+ return (FALSE);
+ }
+ 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, xdr_status, &tx_arg,
+ 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 00000000000..da0ed7073f5
--- /dev/null
+++ b/usr.sbin/rpc.statd/rpc.statd.8
@@ -0,0 +1,113 @@
+.\" $OpenBSD: rpc.statd.8,v 1.1 2008/06/15 04:43:28 sturm 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 by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. 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.
+.\"
+.\"
+.Dd September 19, 1995
+.Dt RPC.STATD 8
+.Os
+.Sh NAME
+.Nm rpc.statd
+.Nd host status monitoring daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Sh DESCRIPTION
+.Nm
+is a daemon which co-operates with rpc.statd 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 restarts, when
+.Nm
+restarts, it will notify all of the hosts which were being monitored
+at the time of the crash.
+.Pp
+Options and operands available for
+.Nm
+:
+.Bl -tag -width Ds
+.It Fl d
+The
+.Fl d
+option causes 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.
+.El
+.Pp
+The
+.Nm
+daemon 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 configured in
+.Xr rc.conf 5
+to run at system startup.
+.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.conf 5 ,
+.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 HISTORY
+A version of
+.Nm
+appeared in
+.Tn SunOS 4 .
+.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 retry 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 00000000000..a6aa05390bb
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.c
@@ -0,0 +1,597 @@
+/* $OpenBSD: statd.c,v 1.1 2008/06/15 04:43:28 sturm Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas. All rights reserved.
+ * 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
+ * This product includes software developed by Christos Zoulas.
+ * 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/param.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <db.h>
+
+#include <rpc/rpc.h>
+
+#include "statd.h"
+
+struct sigaction sa;
+int debug = 0; /* Controls syslog() for debug msgs */
+int _rpcsvcdirty = 0; /* XXX ??? */
+static DB *db; /* Database file */
+
+Header status_info;
+
+static char undefdata[] = "\0\1\2\3\4\5\6\7";
+static DBT undefkey = {
+ undefdata,
+ sizeof(undefdata)
+};
+extern char *__progname;
+
+/* statd.c */
+static int walk_one(int (*fun )(DBT *, HostInfo *, void *), DBT *, DBT *, void *);
+static int walk_db(int (*fun )(DBT *, HostInfo *, void *), void *);
+static int reset_host(DBT *, HostInfo *, void *);
+static int check_work(DBT *, HostInfo *, void *);
+static int unmon_host(DBT *, HostInfo *, void *);
+static int notify_one(DBT *, HostInfo *, void *);
+static void init_file(char *);
+static int notify_one_host(char *);
+static void die(int);
+
+int main(int, char **);
+
+int
+main(int argc, char **argv)
+{
+ SVCXPRT *transp;
+ int ch;
+ struct sigaction nsa;
+
+ while ((ch = getopt(argc, argv, "d")) != (-1)) {
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ default:
+ case '?':
+ fprintf(stderr, "usage: %s [-d]\n", __progname);
+ exit(1);
+ /* NOTREACHED */
+ }
+ }
+ pmap_unset(SM_PROG, SM_VERS);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL) {
+ errx(1, "cannot create udp service.");
+ /* NOTREACHED */
+ }
+ if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_UDP)) {
+ errx(1, "unable to register (SM_PROG, SM_VERS, udp).");
+ /* NOTREACHED */
+ }
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == NULL) {
+ errx(1, "cannot create tcp service.");
+ /* NOTREACHED */
+ }
+ if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_TCP)) {
+ errx(1, "unable to register (SM_PROG, SM_VERS, tcp).");
+ /* NOTREACHED */
+ }
+
+ 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);
+
+ sigemptyset(&nsa.sa_mask);
+ nsa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT;
+ nsa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &nsa, NULL);
+
+ openlog("rpc.statd", 0, LOG_DAEMON);
+ if (debug)
+ syslog(LOG_INFO, "Starting - debug enabled");
+ else
+ syslog(LOG_INFO, "Starting");
+
+ sa.sa_handler = die;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_RESTART;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGALRM);
+
+ /* Initialisation now complete - start operating */
+
+ /* Notify hosts that need it */
+ notify_handler(0);
+
+ while (1)
+ svc_run(); /* Should never return */
+ die(0);
+}
+
+/* notify_handler ---------------------------------------------------------- */
+/*
+ * Purpose: Catch SIGALRM and collect process status
+ * Returns: 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.
+ */
+void
+notify_handler(int sig)
+{
+ time_t now;
+
+ NO_ALARM;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGALRM, &sa, NULL);
+
+ now = time(NULL);
+
+ walk_db(notify_one, &now);
+
+ if (walk_db(check_work, &now) == 0) {
+ /*
+ * No more work to be done.
+ */
+ CLR_ALARM;
+ return;
+ }
+ sync_file();
+ ALARM;
+ alarm(5);
+}
+
+/* sync_file --------------------------------------------------------------- */
+/*
+ * Purpose: Packaged call of msync() to flush changes to mmap()ed file
+ * Returns: Nothing. Errors to syslog.
+ */
+void
+sync_file()
+{
+ DBT data;
+
+ data.data = &status_info;
+ data.size = sizeof(status_info);
+ switch ((*db->put)(db, &undefkey, &data, 0)) {
+ case 0:
+ return;
+ case -1:
+ goto bad;
+ default:
+ abort();
+ }
+ if ((*db->sync)(db, 0) == -1) {
+bad:
+ syslog(LOG_ERR, "database corrupted %m");
+ die(1);
+ }
+}
+
+/* change_host -------------------------------------------------------------- */
+/*
+ * Purpose: Update/Create an entry for host
+ * Returns: Nothing
+ * Notes:
+ *
+ */
+void
+change_host(char *hostnamep, HostInfo *hp)
+{
+ DBT key, data;
+ char *ptr;
+ char hostname[MAXHOSTNAMELEN + 1];
+ HostInfo h;
+
+ strlcpy(hostname, hostnamep, sizeof(hostname));
+ h = *hp;
+
+ for (ptr = hostname; *ptr; ptr++)
+ if (isupper((unsigned char) *ptr))
+ *ptr = tolower((unsigned char) *ptr);
+
+ key.data = hostname;
+ key.size = ptr - hostname + 1;
+ data.data = &h;
+ data.size = sizeof(h);
+
+ switch ((*db->put)(db, &key, &data, 0)) {
+ case -1:
+ syslog(LOG_ERR, "database corrupted %m");
+ die(1);
+ case 0:
+ return;
+ default:
+ abort();
+ }
+}
+
+
+/* find_host -------------------------------------------------------------- */
+/*
+ * Purpose: Find the entry in the status file for a given host
+ * Returns: Copy of entry in hd, or NULL
+ * Notes:
+ *
+ */
+HostInfo *
+find_host(char *hostname, HostInfo *hp)
+{
+ DBT key, data;
+ char *ptr;
+
+ for (ptr = hostname; *ptr; ptr++)
+ if (isupper((unsigned char) *ptr))
+ *ptr = tolower((unsigned char) *ptr);
+
+ key.data = hostname;
+ key.size = ptr - hostname + 1;
+ switch ((*db->get)(db, &key, &data, 0)) {
+ case 0:
+ if (data.size != sizeof(*hp))
+ goto bad;
+ return memcpy(hp, data.data, sizeof(*hp));
+ case 1:
+ return NULL;
+ case -1:
+ goto bad;
+ default:
+ abort();
+ }
+
+bad:
+ syslog(LOG_ERR, "Database corrupted %m");
+ return NULL;
+}
+
+/* walk_one ------------------------------------------------------------- */
+/*
+ * Purpose: Call the given function if the element is valid
+ * Returns: Nothing - exits on error
+ * Notes:
+ */
+static int
+walk_one(int (*fun)(DBT *, HostInfo *, void *), DBT *key, DBT *data, void *ptr)
+{
+ HostInfo h;
+ if (key->size == undefkey.size &&
+ memcmp(key->data, undefkey.data, key->size) == 0)
+ return 0;
+ if (data->size != sizeof(HostInfo)) {
+ syslog(LOG_ERR, "Bad data in database");
+ die(1);
+ }
+ memcpy(&h, data->data, sizeof(h));
+ return (*fun)(key, &h, ptr);
+}
+
+/* walk_db -------------------------------------------------------------- */
+/*
+ * Purpose: Iterate over all elements calling the given function
+ * Returns: -1 if function failed, 0 on success
+ * Notes:
+ */
+static int
+walk_db(int (*fun)(DBT *, HostInfo *, void *), void *ptr)
+{
+ DBT key, data;
+
+ switch ((*db->seq)(db, &key, &data, R_FIRST)) {
+ case -1:
+ goto bad;
+ case 1:
+ /* We should have at least the magic entry at this point */
+ abort();
+ case 0:
+ if (walk_one(fun, &key, &data, ptr) == -1)
+ return -1;
+ break;
+ default:
+ abort();
+ }
+
+ for (;;)
+ switch ((*db->seq)(db, &key, &data, R_NEXT)) {
+ case -1:
+ goto bad;
+ case 0:
+ if (walk_one(fun, &key, &data, ptr) == -1)
+ return -1;
+ break;
+ case 1:
+ return 0;
+ default:
+ abort();
+ }
+bad:
+ syslog(LOG_ERR, "Corrupted database %m");
+ die(1);
+}
+
+/* reset_host ------------------------------------------------------------ */
+/*
+ * Purpose: Clean up existing hosts in file.
+ * Returns: Always success 0.
+ * Notes: 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.
+ */
+static int
+reset_host(DBT *key, HostInfo *hi, void *ptr)
+{
+ if (hi->monList) {
+ hi->notifyReqd = *(time_t *) ptr;
+ hi->attempts = 0;
+ hi->monList = NULL;
+ change_host((char *)key->data, hi);
+ }
+ return 0;
+}
+
+/* check_work ------------------------------------------------------------ */
+/*
+ * Purpose: Check if there is work to be done.
+ * Returns: 0 if there is no work to be done -1 if there is.
+ * Notes:
+ */
+static int
+check_work(DBT *key, HostInfo *hi, void *ptr)
+{
+ return hi->notifyReqd ? -1 : 0;
+}
+
+/* unmon_host ------------------------------------------------------------ */
+/*
+ * Purpose: Unmonitor a host
+ * Returns: 0
+ * Notes:
+ */
+static int
+unmon_host(DBT *key, HostInfo *hi, void *ptr)
+{
+ char *name = key->data;
+
+ if (do_unmon(name, hi, ptr))
+ change_host(name, hi);
+ return 0;
+}
+
+/* notify_one ------------------------------------------------------------ */
+/*
+ * Purpose: Notify one host.
+ * Returns: 0 if success -1 on failure
+ * Notes:
+ */
+static int
+notify_one(DBT *key, HostInfo *hi, void *ptr)
+{
+ time_t now = *(time_t *) ptr;
+ char *name = key->data;
+ int error;
+
+ if (hi->notifyReqd == 0 || hi->notifyReqd > now)
+ return 0;
+
+ /*
+ * If one of the initial attempts fails, we wait
+ * 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.
+ */
+ if (notify_one_host(name) || hi->attempts++ >= 44) {
+ error = 0;
+ hi->notifyReqd = 0;
+ hi->attempts = 0;
+ } else {
+ error = -1;
+ if (hi->attempts < 10)
+ hi->notifyReqd += 5;
+ else if (hi->attempts < 20)
+ hi->notifyReqd += 60;
+ else
+ hi->notifyReqd += 60 * 60;
+ }
+ change_host(name, hi);
+ return error;
+}
+
+/* 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.
+ */
+static void
+init_file(char *filename)
+{
+ DBT data;
+
+ db = dbopen(filename, O_RDWR|O_CREAT|O_NDELAY|O_EXLOCK, 0644, DB_HASH,
+ NULL);
+ if (db == NULL)
+ err(1, "Cannot open `%s'", filename);
+
+ switch ((*db->get)(db, &undefkey, &data, 0)) {
+ case 1:
+ /* New database */
+ memset(&status_info, 0, sizeof(status_info));
+ sync_file();
+ return;
+ case -1:
+ err(1, "error accessing database (%m)");
+ case 0:
+ /* Existing database */
+ if (data.size != sizeof(status_info))
+ errx(1, "database corrupted %lu != %lu",
+ (u_long)data.size, (u_long)sizeof(status_info));
+ memcpy(&status_info, data.data, data.size);
+ break;
+ default:
+ abort();
+ }
+
+ reset_database();
+ return;
+}
+
+/* reset_database --------------------------------------------------------- */
+/*
+ * Purpose: Clears the statd database
+ * Returns: Nothing
+ * Notes: If this is not called on reset, it will leak memory.
+ */
+void
+reset_database(void)
+{
+ time_t now = time(NULL);
+ walk_db(reset_host, &now);
+
+ /* Select the next higher even number for the state counter */
+ status_info.ourState =
+ (status_info.ourState + 2) & 0xfffffffe;
+ status_info.ourState++; /* XXX - ??? */
+ sync_file();
+}
+
+/* unmon_hosts --------------------------------------------------------- */
+/*
+ * Purpose: Unmonitor all the hosts
+ * Returns: Nothing
+ * Notes:
+ */
+void
+unmon_hosts(void)
+{
+ time_t now = time(NULL);
+ walk_db(unmon_host, &now);
+ sync_file();
+}
+
+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[MAXHOSTNAMELEN + 1];
+
+ gethostname(our_hostname, sizeof(our_hostname));
+ our_hostname[sizeof(our_hostname) - 1] = '\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, xdr_stat_chge, &arg, 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);
+}
+
+static void
+die(int n)
+{
+ (*db->close)(db);
+ exit(n);
+}
diff --git a/usr.sbin/rpc.statd/statd.h b/usr.sbin/rpc.statd/statd.h
new file mode 100644
index 00000000000..1b6a35298c3
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.h
@@ -0,0 +1,123 @@
+/* $OpenBSD: statd.h,v 1.1 2008/06/15 04:43:28 sturm 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 "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 Header. 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 */
+ u_char notifyData[16]; /* Opaque data from caller */
+} MonList;
+
+typedef struct {
+ int notifyReqd; /* Time of our next attempt or 0
+ informed the monitored host */
+ int attempts; /* Number of attempts we tried so far */
+ 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 magic; /* Zero magic */
+ int ourState; /* State number as defined in statd protocol */
+} Header;
+
+/* ------------------------------------------------------------------------- */
+
+/* Global variables */
+
+extern int debug; /* = 1 to enable diagnostics to syslog */
+extern struct sigaction sa;
+extern Header status_info;
+
+/* Function prototypes */
+
+/* stat_proc.c */
+struct sm_stat_res *sm_stat_1_svc(sm_name *, struct svc_req *);
+struct sm_stat_res *sm_mon_1_svc(mon *, struct svc_req *);
+struct sm_stat *sm_unmon_1_svc(mon_id *, struct svc_req *);
+struct sm_stat *sm_unmon_all_1_svc(my_id *, struct svc_req *);
+void *sm_simu_crash_1_svc(void *, struct svc_req *);
+void *sm_notify_1_svc(stat_chge *, struct svc_req *);
+int do_unmon(char *, HostInfo *, void *);
+
+/* statd.c */
+void notify_handler(int);
+void sync_file(void);
+void unmon_hosts(void);
+void change_host(char *, HostInfo *);
+HostInfo *find_host(char *, HostInfo *);
+void reset_database(void);
+
+void sm_prog_1(struct svc_req *, SVCXPRT *);
+
+#define NO_ALARM sa.sa_handler == SIG_DFL ? 0 : \
+ (sa.sa_handler = SIG_IGN, sigaction(SIGALRM, &sa, NULL))
+#define ALARM sa.sa_handler == SIG_DFL ? 0 : \
+ (sa.sa_handler = notify_handler, sigaction(SIGALRM, &sa, NULL))
+#define CLR_ALARM sa.sa_handler == SIG_DFL ? 0 : \
+ (sa.sa_handler = SIG_DFL, sigaction(SIGALRM, &sa, NULL))
diff --git a/usr.sbin/rpc.statd/test.c b/usr.sbin/rpc.statd/test.c
new file mode 100644
index 00000000000..c0aad1e8482
--- /dev/null
+++ b/usr.sbin/rpc.statd/test.c
@@ -0,0 +1,126 @@
+/* $OpenBSD: test.c,v 1.1 2008/06/15 04:43:28 sturm Exp $ */
+
+#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(argc, argv)
+ int argc;
+ char **argv;
+{
+ CLIENT *cli;
+ char dummy;
+ void *out;
+ struct mon mon;
+
+ if (argc < 2) {
+ warnx("usage: test {<hostname> | crash}");
+ errx(1, "Always talks to statd at localhost");
+ }
+ printf("Creating client for localhost\n");
+ cli = clnt_create("localhost", SM_PROG, SM_VERS, "udp");
+ if (!cli) {
+ errx(1, "Failed to create client");
+ }
+ 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;
+}