+2004-09-06 Paul Clements <paul.clements@steeleye.com>
+ Neil Brown <neilb@cse.unsw.edu.au>
+
+ * utils/mountd/mountd.c(main): support --ha-callout (-H) for
+ specifying a callout program
+ * utils/mountd/rmtab.c: Call ha_callout on mount/unmount
+ * utils/statd/monitor.c: Call ha_callout on add/del client
+ * utils/statd/rmtcall.c: as above
+ * utils/statd/statd.c: handle --ha-callout (-H)
+ * utils/statd/svc_run.c: call notify_hosts is we have received a
+ sighup
+ * support/include/ha-callout.h: define ha_callout function
+
+
2004-08-31 NeilBrown <neilb@cse.unsw.edu.au>
* utils/mountd/cache.c(cache_process_req): clear fd after
processing so as not to confused libc/sunrpc into thinking
--- /dev/null
+/*
+ * support/include/ha-callout.h
+ *
+ * High Availability NFS Callout support routines
+ *
+ * Copyright (c) 2004, Paul Clements, SteelEye Technology
+ *
+ * In order to implement HA NFS, we need several callouts at key
+ * points in statd and mountd. These callouts all come to ha_callout(),
+ * which, in turn, calls out to an ha-callout script (not part of nfs-utils;
+ * defined by -H argument to rpc.statd and rpc.mountd).
+ */
+#ifndef HA_CALLOUT_H
+#define HA_CALLOUT_H
+
+#include <sys/wait.h>
+
+extern char *ha_callout_prog;
+
+static inline void
+ha_callout(char *event, char *arg1, char *arg2, int arg3)
+{
+ char buf[16]; /* should be plenty */
+ pid_t pid;
+ int ret = -1;
+
+ if (!ha_callout_prog) /* HA callout is not enabled */
+ return;
+
+ sprintf(buf, "%d", arg3);
+
+ pid = fork();
+ switch (pid) {
+ case 0: execl(ha_callout_prog, ha_callout_prog,
+ event, arg1, arg2,
+ arg3 < 0 ? NULL : buf,
+ NULL);
+ perror("execl");
+ exit(2);
+ case -1: perror("fork");
+ break;
+ default: ret = waitpid(pid, NULL, 0);
+ }
+
+#ifdef dprintf
+ dprintf(N_DEBUG, "ha callout returned %d\n", WEXITSTATUS(ret));
+#else
+ xlog(D_GENERAL, "ha callout returned %d\n", WEXITSTATUS(ret));
+#endif
+}
+
+#endif
int new_cache = 0;
+/* PRC: a high-availability callout program can be specified with -H
+ * When this is done, the program will receive callouts whenever clients
+ * send mount or unmount requests -- the callout is not needed for 2.6 kernel */
+char *ha_callout_prog = NULL;
+
static struct option longopts[] =
{
{ "foreground", 0, 0, 'F' },
{ "version", 0, 0, 'v' },
{ "port", 1, 0, 'p' },
{ "no-tcp", 0, 0, 'n' },
+ { "ha-callout", 1, 0, 'H' },
{ NULL, 0, 0, 0 }
};
/* Parse the command line options and arguments. */
opterr = 0;
- while ((c = getopt_long(argc, argv, "o:n:Fd:f:p:P:hN:V:v", longopts, NULL)) != EOF)
+ while ((c = getopt_long(argc, argv, "o:n:Fd:f:p:P:hH:N:V:v", longopts, NULL)) != EOF)
switch (c) {
case 'o':
descriptors = atoi(optarg);
case 'f':
export_file = optarg;
break;
+ case 'H': /* PRC: specify a high-availability callout program */
+ ha_callout_prog = optarg;
+ break;
case 'h':
usage(argv [0], 0);
break;
"Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n"
" [-o num|--descriptors num] [-f exports-file|--exports-file=file]\n"
" [-p|--port port] [-V version|--nfs-version version]\n"
-" [-N version|--no-nfs-version version] [-n|--no-tcp]\n", prog);
+" [-N version|--no-nfs-version version] [-n|--no-tcp]\n"
+" [-H ha-callout-prog]\n", prog);
exit(n);
}
.\" mountd(8)
.\"
.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
-.TH rpc.mountd 8 "25 Aug 2000"
+.\" Modified by Paul Clements, 2004.
+.TH rpc.mountd 8 "31 Aug 2004"
.SH NAME
rpc.mountd \- NFS mount daemon
.SH SYNOPSIS
to bind to the specified port num, instead of using the random port
number assigned by the portmapper.
.TP
+.B \-H " or " \-\-ha-callout prog
+Specify a high availability callout program, which will receive callouts
+for all client mount and unmount requests. This allows
+.B rpc.mountd
+to be used in a High Availability NFS (HA-NFS) environment. This callout is not
+needed (and should not be used) with 2.6 and later kernels (instead,
+mount the nfsd filesystem on
+.B /proc/fs/nfsd
+).
+The program will be called with 4 arguments.
+The first will be
+.B mount
+or
+.B unmount
+depending on the reason for the callout.
+The second will be the name of the client performing the mount.
+The third will be the path that the client is mounting.
+The last is the number of concurrent mounts that we believe the client
+has of that path.
+.TP
.B \-V " or " \-\-nfs-version
This option can be used to request that
.B rpc.mountd
#include "exportfs.h"
#include "xio.h"
#include "mountd.h"
+#include "ha-callout.h"
#include <limits.h> /* PATH_MAX */
host) == 0
&& strcmp(rep->r_path, path) == 0) {
rep->r_count++;
+ /* PRC: do the HA callout: */
+ ha_callout("mount", rep->r_client, rep->r_path, rep->r_count);
putrmtabent(rep, &pos);
endrmtabent();
xfunlock(lockid);
xe.r_path [sizeof (xe.r_path) - 1] = '\0';
xe.r_count = 1;
if (setrmtabent("a")) {
+ /* PRC: do the HA callout: */
+ ha_callout("mount", xe.r_client, xe.r_path, xe.r_count);
putrmtabent(&xe, NULL);
endrmtabent();
}
while ((rep = getrmtabent(1, NULL)) != NULL) {
match = !strcmp (rep->r_client, hname)
&& !strcmp(rep->r_path, path);
- if (match)
+ if (match) {
rep->r_count--;
+ /* PRC: do the HA callout: */
+ ha_callout("unmount", rep->r_client, rep->r_path, rep->r_count);
+ }
if (!match || rep->r_count)
fputrmtabent(fp, rep, NULL);
}
#include "misc.h"
#include "statd.h"
#include "notlist.h"
+#include "ha-callout.h"
notify_list * rtnl = NULL; /* Run-time notify list. */
goto failure;
}
free(path);
+ /* PRC: do the HA callout: */
+ ha_callout("add-client", mon_name, my_name, -1);
nlist_insert(&rtnl, clnt);
close(fd);
/* Match! */
dprintf(N_DEBUG, "UNMONITORING %s for %s",
mon_name, my_name);
+
+ /* PRC: do the HA callout: */
+ ha_callout("del-client", mon_name, my_name, -1);
+
nlist_free(&rtnl, clnt);
xunlink(SM_DIR, mon_name, 1);
sizeof (mon_name) - 1);
mon_name[sizeof (mon_name) - 1] = '\0';
temp = NL_NEXT(clnt);
+ /* PRC: do the HA callout: */
+ ha_callout("del-client", mon_name, argp->my_name, -1);
nlist_free(&rtnl, clnt);
xunlink(SM_DIR, mon_name, 1);
++count;
#include "statd.h"
#include "notlist.h"
#include "log.h"
+#include "ha-callout.h"
#define MAXMSGSIZE (2048 / sizeof(unsigned int))
note(N_ERROR,
"Can't notify %s, giving up.",
NL_MON_NAME(entry));
+ /* PRC: do the HA callout */
+ ha_callout("del-client", NL_MON_NAME(entry), NL_MY_NAME(entry), -1);
xunlink(SM_BAK_DIR, NL_MON_NAME(entry), 0);
nlist_free(¬ify, entry);
}
char *name_p = NULL;
char *version_p = NULL;
+/* PRC: a high-availability callout program can be specified with -H
+ * When this is done, the program will receive callouts whenever clients
+ * are added or deleted to the notify list */
+char *ha_callout_prog = NULL;
+
static struct option longopts[] =
{
{ "foreground", 0, 0, 'F' },
{ "name", 1, 0, 'n' },
{ "state-directory-path", 1, 0, 'P' },
{ "notify-mode", 0, 0, 'N' },
+ { "ha-callout", 1, 0, 'H' },
{ NULL, 0, 0, 0 }
};
exit (0);
}
+static void
+sigusr (int sig)
+{
+ dprintf (N_DEBUG, "Caught signal %d, re-reading notify list.", sig);
+ re_notify = 1;
+}
+
/*
* Startup information.
*/
fprintf(stderr," -n, --name Specify a local hostname.\n");
fprintf(stderr," -P State directory path.\n");
fprintf(stderr," -N Run in notify only mode.\n");
+ fprintf(stderr," -H Specify a high-availability callout program.\n");
}
static const char *pidfile = "/var/run/rpc.statd.pid";
MY_NAME = NULL;
/* Process command line switches */
- while ((arg = getopt_long(argc, argv, "h?vVFNdn:p:o:P:", longopts, NULL)) != EOF) {
+ while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:", longopts, NULL)) != EOF) {
switch (arg) {
case 'V': /* Version */
case 'v':
sprintf(SM_STAT_PATH, "%s/state", DIR_BASE );
}
break;
+ case 'H': /* PRC: specify the ha-callout program */
+ if ((ha_callout_prog = xstrdup(optarg)) == NULL) {
+ fprintf(stderr, "%s: xstrdup(%s) failed!\n",
+ argv[0], optarg);
+ exit(1);
+ }
+ break;
case '?': /* heeeeeelllllllpppp? heh */
case 'h':
usage();
signal (SIGHUP, killer);
signal (SIGINT, killer);
signal (SIGTERM, killer);
+ /* PRC: trap SIGUSR1 to re-read notify list from disk */
+ signal(SIGUSR1, sigusr);
/* WARNING: the following works on Linux and SysV, but not BSD! */
signal(SIGCHLD, SIG_IGN);
*/
extern char *name_p; /* program basename */
extern char *version_p; /* program version */
+
+extern int re_notify; /* time to re-read notify list */
.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
.\" Modified by Jeffrey A. Uphoff, 1999, 2002.
.\" Modified by Lon Hohberger, 2000.
-.TH rpc.statd 8 "16 Sep 2002"
+.\" Modified by Paul Clements, 2004.
+.TH rpc.statd 8 "31 Aug 2004"
.SH NAME
rpc.statd \- NSM status monitor
.SH SYNOPSIS
-.B "/sbin/rpc.statd [-F] [-d] [-?] [-n " name "] [-o " port "] [-p " port "] [-V]"
+.B "/sbin/rpc.statd [-F] [-d] [-?] [-n " name "] [-o " port "] [-p " port "] [-H " prog "] [-V]"
.SH DESCRIPTION
The
.B rpc.statd
monitored nodes, and exit once the notifications have been sent. This mode is
used to enable Highly Available NFS implementations (i.e. HA-NFS).
.TP
+.BI "\-H, " "" " \-\-ha-callout " prog
+Specify a high availability callout program, which will receive callouts
+for all client monitor and unmonitor requests. This allows
+.B rpc.statd
+to be used in a High Availability NFS (HA-NFS) environment. The
+program will be run with 3 arguments: The first is either
+.B add-client
+or
+.B del-client
+depending on the reason for the callout.
+The second will be the name of the client.
+The third will be the name of the server as known to the client.
+.TP
.B -?
Causes
.B rpc.statd
.BR hosts_access (5)
manual pages.
+.SH SIGNALS
+.BR SIGUSR1
+causes
+.B rpc.statd
+to re-read the notify list from disk
+and send notifications to clients. This can be used in High Availability NFS
+(HA-NFS) environments to notify clients to reacquire file locks upon takeover
+of an NFS export from another server.
+
.SH FILES
.BR /var/lib/nfs/state
.br
H.J. Lu <hjl@gnu.org>
.br
Lon Hohberger <hohberger@missioncriticallinux.com>
+.br
+Paul Clements <paul.clements@steeleye.com>
* requests are put.
*/
notify_list * notify = NULL;
+int re_notify = 0;
/*
* Jump-off function.
for (;;) {
if (svc_stop)
return;
+ if (re_notify) {
+ notify_hosts();
+ re_notify = 0;
+ }
/* Ah, there are some notifications to be processed */
while (notify && NL_WHEN(notify) <= time(&now)) {