]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/statd/sm-notify.c
sm-notify - fix bugs related to run-only-once.
[nfs-utils.git] / utils / statd / sm-notify.c
index 7af0ceacd0c68957fd47a2e095d39d938f13e6f2..0eb0b09e5cdf28ca70ee7eb4030af634db594833 100644 (file)
 #include <stdarg.h>
 #include <netdb.h>
 #include <errno.h>
+#include <grp.h>
 
 #ifndef BASEDIR
-#define BASEDIR                "/var/lib/nfs"
+# ifdef NFS_STATEDIR
+#  define BASEDIR              NFS_STATEDIR
+# else
+#  define BASEDIR              "/var/lib/nfs"
+# endif
 #endif
 
-#define _SM_STATE_PATH BASEDIR "/state"
-#define        _SM_DIR_PATH    BASEDIR "/sm"
-#define        _SM_BAK_PATH    _SM_DIR_PATH ".bak"
+#define DEFAULT_SM_STATE_PATH  BASEDIR "/state"
+#define        DEFAULT_SM_DIR_PATH     BASEDIR "/sm"
+#define        DEFAULT_SM_BAK_PATH     DEFAULT_SM_DIR_PATH ".bak"
+
+char *_SM_BASE_PATH = BASEDIR;
+char *_SM_STATE_PATH = DEFAULT_SM_STATE_PATH;
+char *_SM_DIR_PATH = DEFAULT_SM_DIR_PATH;
+char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH;
 
 #define NSM_PROG       100024
 #define NSM_PROGRAM    100024
@@ -71,11 +81,12 @@ static void         backup_hosts(const char *, const char *);
 static void            get_hosts(const char *);
 static void            insert_host(struct nsm_host *);
 struct nsm_host *      find_host(uint32_t);
-static int             addr_parse(int, const char *, nsm_address *);
 static int             addr_get_port(nsm_address *);
 static void            addr_set_port(nsm_address *, int);
 static int             host_lookup(int, const char *, nsm_address *);
 void                   nsm_log(int fac, const char *fmt, ...);
+static int             record_pid();
+static void            drop_privs(void);
 
 static struct nsm_host *       hosts = NULL;
 
@@ -83,9 +94,13 @@ int
 main(int argc, char **argv)
 {
        int     c;
+       int     force = 0;
 
-       while ((c = getopt(argc, argv, "dm:np:v:q")) != -1) {
+       while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) {
                switch (c) {
+               case 'f':
+                       force = 1;
+                       break;
                case 'd':
                        opt_debug++;
                        break;
@@ -104,16 +119,41 @@ main(int argc, char **argv)
                case 'q':
                        opt_quiet = 1;
                        break;
+               case 'P':
+                       _SM_BASE_PATH = strdup(optarg);
+                       _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state"));
+                       _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm"));
+                       _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak"));
+                       if (_SM_BASE_PATH == NULL ||
+                           _SM_STATE_PATH == NULL ||
+                           _SM_DIR_PATH == NULL ||
+                           _SM_BAK_PATH == NULL) {
+                               nsm_log(LOG_WARNING, "unable to allocate memory");
+                               exit(1);
+                       }
+                       strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state");
+                       strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm");
+                       strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak");
+                       break;
+
                default:
                        goto usage;
                }
        }
 
        if (optind < argc) {
-usage:         fprintf(stderr, "sm-notify [-d]\n");
+usage:         fprintf(stderr,
+                       "Usage: sm-notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
+                       "            [-P /path/to/state/directory] [-v my_host_name]\n");
                return 1;
        }
 
+       if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) {
+               if (record_pid() == 0 && force == 0 && opt_update_state == 1)
+                       /* already run, don't try again */
+                       exit(0);
+       }
+
        if (opt_srcaddr) {
                strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
        } else
@@ -185,8 +225,7 @@ notify(void)
 
        /* Bind source IP if provided on command line */
        if (opt_srcaddr) {
-               if (!addr_parse(AF_INET, opt_srcaddr, &local_addr)
-                && !host_lookup(AF_INET, opt_srcaddr, &local_addr)) {
+               if (!host_lookup(AF_INET, opt_srcaddr, &local_addr)) {
                        nsm_log(LOG_WARNING,
                                "Not a valid hostname or address: \"%s\"\n",
                                opt_srcaddr);
@@ -210,6 +249,8 @@ notify(void)
        if (opt_max_retry)
                failtime = time(NULL) + opt_max_retry;
 
+       drop_privs();
+
        while (hosts) {
                struct pollfd   pfd;
                time_t          now = time(NULL);
@@ -461,9 +502,7 @@ get_hosts(const char *dirname)
                        host = calloc(1, sizeof(*host));
 
                snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
-               if (!addr_parse(AF_INET, de->d_name, &host->addr)
-                && !addr_parse(AF_INET6, de->d_name, &host->addr)
-                && !host_lookup(AF_INET, de->d_name, &host->addr)) {
+               if (!host_lookup(AF_UNSPEC, de->d_name, &host->addr)) {
                        nsm_log(LOG_WARNING,
                                "%s doesn't seem to be a valid address, skipped",
                                de->d_name);
@@ -594,22 +633,6 @@ nsm_get_state(int update)
 /*
  * Address handling utilities
  */
-static int
-addr_parse(int af, const char *name, nsm_address *addr)
-{
-       void    *ptr;
-
-       if (af == AF_INET)
-               ptr = &((struct sockaddr_in *) addr)->sin_addr;
-       else if (af == AF_INET6)
-               ptr = &((struct sockaddr_in6 *) addr)->sin6_addr;
-       else
-               return 0;
-       if (inet_pton(af, name, ptr) <= 0)
-               return 0;
-       ((struct sockaddr *) addr)->sa_family = af;
-       return 1;
-}
 
 int
 addr_get_port(nsm_address *addr)
@@ -676,3 +699,51 @@ nsm_log(int fac, const char *fmt, ...)
        }
        va_end(ap);
 }
+
+/*
+ * Record pid in /var/run/sm-notify.pid
+ * This file should remain until a reboot, even if the
+ * program exits.
+ * If file already exists, fail.
+ */
+static int record_pid()
+{
+       char pid[20];
+       int fd;
+
+       snprintf(pid, 20, "%d\n", getpid());
+       fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
+       if (fd < 0)
+               return 0;
+       write(fd, pid, strlen(pid));
+       close(fd);
+       return 1;
+}
+
+/* Drop privileges to match owner of state-directory
+ * (in case a reply triggers some unknown bug).
+ */
+static void drop_privs(void)
+{
+       struct stat st;
+
+       if (stat(_SM_DIR_PATH, &st) == -1 &&
+           stat(_SM_BASE_PATH, &st) == -1) {
+               st.st_uid = 0;
+               st.st_gid = 0;
+       }
+
+       if (st.st_uid == 0) {
+               nsm_log(LOG_WARNING,
+                       "sm-notify running as root. chown %s to choose different user\n",
+                   _SM_DIR_PATH);
+               return;
+       }
+
+       setgroups(0, NULL);
+       if (setgid(st.st_gid) == -1
+           || setuid(st.st_uid) == -1) {
+               nsm_log(LOG_ERR, "Fail to drop privileges");
+               exit(1);
+       }
+}