* NSM for Linux.
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/stat.h>
#include <limits.h>
#include <signal.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#include <rpcmisc.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
#include <grp.h>
#include "statd.h"
#include "version.h"
+#include "nfslib.h"
/* Socket operations */
#include <sys/types.h>
/* ----- end of state directory path stuff ------- */
-short int restart = 0;
int run_mode = 0; /* foreground logging mode */
/* LH - I had these local to main, but it seemed silly to have
* two copies of each - one in main(), one static in log.c...
* It also eliminates the 256-char static in log.c */
char *name_p = NULL;
-char *version_p = NULL;
+const 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[] =
{
{ "name", 1, 0, 'n' },
{ "state-directory-path", 1, 0, 'P' },
{ "notify-mode", 0, 0, 'N' },
+ { "ha-callout", 1, 0, 'H' },
+ { "no-notify", 0, 0, 'L' },
{ NULL, 0, 0, 0 }
};
extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
-extern int statd_get_socket(int port);
+extern int statd_get_socket(void);
+static void load_state_number(void);
#ifdef SIMULATIONS
extern void simulator (int, char **);
killer (int sig)
{
note (N_FATAL, "Caught signal %d, un-registering and exiting.", sig);
- if (!(run_mode & MODE_NOTIFY_ONLY))
- pmap_unset (SM_PROG, SM_VERS);
+ pmap_unset (SM_PROG, SM_VERS);
exit (0);
}
+static void
+sigusr (int sig)
+{
+ extern void my_svc_exit (void);
+ dprintf (N_DEBUG, "Caught signal %d, re-notifying (state %d).", sig,
+ MY_STATE);
+ my_svc_exit();
+}
+
/*
* Startup information.
*/
if (run_mode & MODE_LOG_STDERR)
strcat(buf,"Log-STDERR ");
- if (run_mode & MODE_NOTIFY_ONLY)
- {
- strcat(buf,"Notify-Only ");
- }
note(N_WARNING,buf);
- /* future: IP aliasing
- if (run_mode & MODE_NOTIFY_ONLY)
- {
- dprintf(N_DEBUG,"Notify IP: %s",svr_addr);
- } */
}
/*
* help the occasional admin.
*/
static void
-usage()
+usage(void)
{
fprintf(stderr,"usage: %s [options]\n", name_p);
fprintf(stderr," -h, -?, --help Print this help screen.\n");
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," -L, --no-notify Do not perform any notification.\n");
+ fprintf(stderr," -H Specify a high-availability callout program.\n");
}
static const char *pidfile = "/var/run/rpc.statd.pid";
struct stat st;
if (stat(SM_DIR, &st) == -1 &&
- stat(DIR_BASE, &st) == -1)
+ stat(DIR_BASE, &st) == -1) {
st.st_uid = 0;
+ st.st_gid = 0;
+ }
if (st.st_uid == 0) {
note(N_WARNING, "statd running as root. chown %s to choose different user\n",
}
}
+static void run_sm_notify(int outport)
+{
+ char op[20];
+ char *av[6];
+ int ac = 0;
+
+ av[ac++] = "/usr/sbin/sm-notify";
+ if (run_mode & MODE_NODAEMON)
+ av[ac++] = "-d";
+ if (outport) {
+ sprintf(op, "-p%d", outport);
+ av[ac++] = op;
+ }
+ if (run_mode & STATIC_HOSTNAME) {
+ av[ac++] = "-v";
+ av[ac++] = MY_NAME;
+ }
+ av[ac] = NULL;
+ execv(av[0], av);
+ fprintf(stderr, "%s: failed to run %s\n", name_p, av[0]);
+ exit(2);
+
+}
/*
* Entry routine/main loop.
*/
int pid;
int arg;
int port = 0, out_port = 0;
+ struct rlimit rlim;
int pipefds[2] = { -1, -1};
char status;
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:L", longopts, NULL)) != EOF) {
switch (arg) {
case 'V': /* Version */
case 'v':
case 'N':
run_mode |= MODE_NOTIFY_ONLY;
break;
+ case 'L': /* Listen only */
+ run_mode |= MODE_NO_NOTIFY;
+ break;
case 'd': /* No daemon only - log to stderr */
run_mode |= MODE_LOG_STDERR;
break;
}
break;
case 'n': /* Specify local hostname */
+ run_mode |= STATIC_HOSTNAME;
MY_NAME = xstrdup(optarg);
break;
case 'P':
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();
exit(-1);
}
+ if (run_mode & MODE_NOTIFY_ONLY) {
+ fprintf(stderr, "%s: -N deprecated, consider using /usr/sbin/sm-notify directly\n",
+ name_p);
+ run_sm_notify(out_port);
+ }
+
+
if (!(run_mode & MODE_NODAEMON)) {
run_mode &= ~MODE_LOG_STDERR; /* Never log to console in
daemon mode. */
}
+ if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
+ fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
+ argv [0], strerror(errno));
+ else {
+ /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
+ if (rlim.rlim_cur > FD_SETSIZE) {
+ rlim.rlim_cur = FD_SETSIZE;
+
+ if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
+ fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
+ argv [0], strerror(errno));
+ }
+ }
+ }
+
#ifdef SIMULATIONS
if (argc > 1)
/* LH - I _really_ need to update simulator... */
#endif
if (!(run_mode & MODE_NODAEMON)) {
- int filedes, fdmax, tempfd;
+ int tempfd;
if (pipe(pipefds)<0) {
perror("statd: unable to create pipe");
}
}
tempfd = open("/dev/null", O_RDWR);
- close(0); dup2(tempfd, 0);
- close(1); dup2(tempfd, 1);
- close(2); dup2(tempfd, 2);
- fdmax = sysconf (_SC_OPEN_MAX);
- for (filedes = 3; filedes < fdmax; filedes++)
- if (filedes != pipefds[1])
- close (filedes);
-
+ dup2(tempfd, 0);
+ dup2(tempfd, 1);
+ dup2(tempfd, 2);
+ dup2(pipefds[1], 3);
+ pipefds[1] = 3;
+ closeall(4);
}
/* Child. */
- log_init (name_p,version_p);
+ log_init (/*name_p,version_p*/);
log_modes();
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);
-
- /* initialize out_port */
- statd_get_socket(out_port);
+ /*
+ * Ignore SIGPIPE to avoid statd dying when peers close their
+ * TCP connection while we're trying to reply to them.
+ */
+ signal(SIGPIPE, SIG_IGN);
create_pidfile();
atexit(truncate_pidfile);
- drop_privs();
- for (;;) {
- if (!(run_mode & MODE_NOTIFY_ONLY)) {
- /* Do not do pmap_unset() when running in notify mode.
- * We may clear the portmapper record for a statd not
- * running in notify mode disabling it.
- * Juan C. Gomez j_carlos_gomez@yahoo.com
- */
- pmap_unset (SM_PROG, SM_VERS);
+ if (! (run_mode & MODE_NO_NOTIFY))
+ switch (pid = fork()) {
+ case 0:
+ run_sm_notify(out_port);
+ break;
+ case -1:
+ break;
+ default:
+ waitpid(pid, NULL, 0);
}
- change_state ();
- shuffle_dirs (); /* Move directory names around */
- /* If we got this far, we have successfully started, so notify parent */
- if (pipefds[1] > 0) {
- status = 0;
- write(pipefds[1], &status, 1);
- close(pipefds[1]);
- pipefds[1] = -1;
- }
+ /* Make sure we have a privilege port for calling into the kernel */
+ statd_get_socket();
- notify_hosts (); /* Send out notify requests */
- ++restart;
+ /* If sm-notify didn't take all the state files, load
+ * state information into our notify-list so we can
+ * pass on any SM_NOTIFY that arrives
+ */
+ load_state();
+ load_state_number();
+ pmap_unset (SM_PROG, SM_VERS);
+
+ /* this registers both UDP and TCP services */
+ rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
+
+ /* If we got this far, we have successfully started, so notify parent */
+ if (pipefds[1] > 0) {
+ status = 0;
+ write(pipefds[1], &status, 1);
+ close(pipefds[1]);
+ pipefds[1] = -1;
+ }
- /* this registers both UDP and TCP services */
- if (!(run_mode & MODE_NOTIFY_ONLY)) {
- rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
- }
+ drop_privs();
+ for (;;) {
/*
* Handle incoming requests: SM_NOTIFY socket requests, as
* well as callbacks from lockd.
*/
my_svc_run(); /* I rolled my own, Olaf made it better... */
- if ((run_mode & MODE_NOTIFY_ONLY))
- break;
+ /* Only get here when simulating a crash so we should probably
+ * start sm-notify running again. As we have already dropped
+ * privileges, this might not work, but I don't think
+ * responding to SM_SIMU_CRASH is an important use cases to
+ * get perfect.
+ */
+ if (! (run_mode & MODE_NO_NOTIFY))
+ switch (pid = fork()) {
+ case 0:
+ run_sm_notify(out_port);
+ break;
+ case -1:
+ break;
+ default:
+ waitpid(pid, NULL, 0);
+ }
+
}
return 0;
}
+
+static void
+load_state_number(void)
+{
+ int fd;
+
+ if ((fd = open(SM_STAT_PATH, O_RDONLY)) == -1)
+ return;
+
+ read(fd, &MY_STATE, sizeof(MY_STATE));
+ close(fd);
+ fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY);
+ if (fd >= 0) {
+ char buf[20];
+ snprintf(buf, sizeof(buf), "%d", MY_STATE);
+ write(fd, buf, strlen(buf));
+ close(fd);
+ }
+
+}