]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/mount/stropts.c
Steinar Gunderson reports:
[nfs-utils.git] / utils / mount / stropts.c
index 72480b7e0d279429b870d5b0a3db2107b95081cf..ad5bdee598c243c5c1f05903d75121be3c0f3eac 100644 (file)
  *
  */
 
  *
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <ctype.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <unistd.h>
 #include <stdio.h>
 #define NFS_MAXPATHNAME                (1024)
 #endif
 
 #define NFS_MAXPATHNAME                (1024)
 #endif
 
+#ifndef NFS_DEF_FG_TIMEOUT_MINUTES
+#define NFS_DEF_FG_TIMEOUT_MINUTES     (2u)
+#endif
+
+#ifndef NFS_DEF_BG_TIMEOUT_MINUTES
+#define NFS_DEF_BG_TIMEOUT_MINUTES     (10000u)
+#endif
+
 extern int nfs_mount_data_version;
 extern char *progname;
 extern int verbose;
 extern int nfs_mount_data_version;
 extern char *progname;
 extern int verbose;
@@ -136,6 +148,32 @@ static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
        return 1;
 }
 
        return 1;
 }
 
+/*
+ * Obtain a retry timeout value based on the value of the "retry=" option.
+ *
+ * Returns a time_t timeout timestamp, in seconds.
+ */
+static time_t nfs_parse_retry_option(struct mount_options *options,
+                                    unsigned int timeout_minutes)
+{
+       char *retry_option, *endptr;
+
+       retry_option = po_get(options, "retry");
+       if (retry_option) {
+               long tmp;
+
+               errno = 0;
+               tmp = strtol(retry_option, &endptr, 10);
+               if (errno == 0 && endptr != retry_option && tmp >= 0)
+                       timeout_minutes = tmp;
+               else if (verbose)
+                       nfs_error(_("%s: invalid retry timeout was specified; "
+                                       "using default timeout"), progname);
+       }
+
+       return time(NULL) + (time_t)(timeout_minutes * 60);
+}
+
 /*
  * Append the 'addr=' option to the options string to pass a resolved
  * server address to the kernel.  After a successful mount, this address
 /*
  * Append the 'addr=' option to the options string to pass a resolved
  * server address to the kernel.  After a successful mount, this address
@@ -214,14 +252,35 @@ static int fix_mounthost_option(struct mount_options *options)
        return 0;
 }
 
        return 0;
 }
 
+/*
+ * Returns zero if the "lock" option is in effect, but statd
+ * can't be started.  Otherwise, returns 1.
+ */
+static int verify_lock_option(struct mount_options *options)
+{
+       if (po_rightmost(options, "nolock", "lock") == PO_KEY1_RIGHTMOST)
+               return 1;
+
+       if (!start_statd()) {
+               nfs_error(_("%s: rpc.statd is not running but is "
+                           "required for remote locking."), progname);
+               nfs_error(_("%s: Either use '-o nolock' to keep "
+                           "locks local, or start statd."), progname);
+               return 0;
+       }
+
+       return 1;
+}
+
 /*
  * Set up mandatory mount options.
  *
  * Returns 1 if successful; otherwise zero.
  */
 /*
  * Set up mandatory mount options.
  *
  * Returns 1 if successful; otherwise zero.
  */
-static int set_mandatory_options(const char *type,
-                                struct sockaddr_in *saddr,
-                                struct mount_options *options)
+static int validate_options(const char *type,
+                           struct sockaddr_in *saddr,
+                           struct mount_options *options,
+                           int fake)
 {
        if (!append_addr_option(saddr, options))
                return 0;
 {
        if (!append_addr_option(saddr, options))
                return 0;
@@ -232,6 +291,8 @@ static int set_mandatory_options(const char *type,
        } else {
                if (!fix_mounthost_option(options))
                        return 0;
        } else {
                if (!fix_mounthost_option(options))
                        return 0;
+               if (!fake && !verify_lock_option(options))
+                       return 0;
        }
 
        return 1;
        }
 
        return 1;
@@ -248,7 +309,6 @@ static int set_mandatory_options(const char *type,
 static int is_permanent_error(int error)
 {
        switch (error) {
 static int is_permanent_error(int error)
 {
        switch (error) {
-       case EACCES:
        case ESTALE:
        case ETIMEDOUT:
        case ECONNREFUSED:
        case ESTALE:
        case ETIMEDOUT:
        case ECONNREFUSED:
@@ -304,9 +364,6 @@ static struct mount_options *rewrite_mount_options(char *str)
        if (option)
                mnt_server.pmap.pm_port = atoi(option);
        mnt_server.pmap.pm_prog = MOUNTPROG;
        if (option)
                mnt_server.pmap.pm_port = atoi(option);
        mnt_server.pmap.pm_prog = MOUNTPROG;
-       option = po_get(options, "mountprog");
-       if (option)
-               mnt_server.pmap.pm_prog = atoi(option);
        option = po_get(options, "mountvers");
        if (option)
                mnt_server.pmap.pm_vers = atoi(option);
        option = po_get(options, "mountvers");
        if (option)
                mnt_server.pmap.pm_vers = atoi(option);
@@ -317,9 +374,6 @@ static struct mount_options *rewrite_mount_options(char *str)
                po_remove_all(options, "port");
        }
        nfs_server.pmap.pm_prog = NFS_PROGRAM;
                po_remove_all(options, "port");
        }
        nfs_server.pmap.pm_prog = NFS_PROGRAM;
-       option = po_get(options, "nfsprog");
-       if (option)
-               nfs_server.pmap.pm_prog = atoi(option);
 
        option = po_get(options, "nfsvers");
        if (option) {
 
        option = po_get(options, "nfsvers");
        if (option) {
@@ -537,14 +591,9 @@ static int nfsmount_fg(const char *spec, const char *node,
                       char **extra_opts)
 {
        unsigned int secs = 1;
                       char **extra_opts)
 {
        unsigned int secs = 1;
-       time_t timeout = time(NULL);
-       char *retry;
-
-       timeout += 60 * 2;              /* default: 2 minutes */
-       retry = po_get(options, "retry");
-       if (retry)
-               timeout += 60 * atoi(retry);
+       time_t timeout;
 
 
+       timeout = nfs_parse_retry_option(options, NFS_DEF_FG_TIMEOUT_MINUTES);
        if (verbose)
                printf(_("%s: timeout set for %s"),
                        progname, ctime(&timeout));
        if (verbose)
                printf(_("%s: timeout set for %s"),
                        progname, ctime(&timeout));
@@ -575,6 +624,91 @@ static int nfsmount_fg(const char *spec, const char *node,
        return EX_FAIL;
 }
 
        return EX_FAIL;
 }
 
+/*
+ * Handle "background" NFS mount [first try]
+ *
+ * Returns a valid mount command exit code.
+ *
+ * EX_BG should cause the caller to fork and invoke nfsmount_child.
+ */
+static int nfsmount_parent(const char *spec, const char *node,
+                          const char *type, char *hostname, int flags,
+                          struct mount_options *options,
+                          int fake, char **extra_opts)
+{
+       if (try_mount(spec, node, type, flags, options,
+                                       fake, extra_opts))
+               return EX_SUCCESS;
+
+       if (is_permanent_error(errno)) {
+               mount_error(spec, node, errno);
+               return EX_FAIL;
+       }
+
+       sys_mount_errors(hostname, errno, 1, 1);
+       return EX_BG;
+}
+
+/*
+ * Handle "background" NFS mount [retry daemon]
+ *
+ * Returns a valid mount command exit code: EX_SUCCESS if successful,
+ * EX_FAIL if a failure occurred.  There's nothing to catch the
+ * error return, though, so we use sys_mount_errors to log the
+ * failure.
+ */
+static int nfsmount_child(const char *spec, const char *node,
+                         const char *type, char *hostname, int flags,
+                         struct mount_options *options,
+                         int fake, char **extra_opts)
+{
+       unsigned int secs = 1;
+       time_t timeout;
+
+       timeout = nfs_parse_retry_option(options, NFS_DEF_BG_TIMEOUT_MINUTES);
+
+       for (;;) {
+               if (sleep(secs))
+                       break;
+               secs <<= 1;
+               if (secs > 120)
+                       secs = 120;
+
+               if (try_mount(spec, node, type, flags, options,
+                                                       fake, extra_opts))
+                       return EX_SUCCESS;
+
+               if (is_permanent_error(errno))
+                       break;
+
+               if (time(NULL) > timeout)
+                       break;
+
+               sys_mount_errors(hostname, errno, 1, 1);
+       };
+
+       sys_mount_errors(hostname, errno, 1, 0);
+       return EX_FAIL;
+}
+
+/*
+ * Handle "background" NFS mount
+ *
+ * Returns a valid mount command exit code.
+ */
+static int nfsmount_bg(const char *spec, const char *node,
+                         const char *type, char *hostname, int flags,
+                         struct mount_options *options,
+                         int fake, int child, char **extra_opts)
+{
+       if (!child)
+               return nfsmount_parent(spec, node, type, hostname, flags,
+                                       options, fake, extra_opts);
+       else
+               return nfsmount_child(spec, node, type, hostname, flags,
+                                       options, fake, extra_opts);
+}
+
 /**
  * nfsmount_string - Mount an NFS file system using C string options
  * @spec: C string specifying remote share to mount ("hostname:path")
 /**
  * nfsmount_string - Mount an NFS file system using C string options
  * @spec: C string specifying remote share to mount ("hostname:path")
@@ -592,32 +726,32 @@ int nfsmount_string(const char *spec, const char *node, const char *type,
        struct mount_options *options = NULL;
        struct sockaddr_in saddr;
        char *hostname;
        struct mount_options *options = NULL;
        struct sockaddr_in saddr;
        char *hostname;
-       int err, retval = EX_FAIL;
+       int retval = EX_FAIL;
 
        if (!parse_devname(spec, &hostname))
 
        if (!parse_devname(spec, &hostname))
-               goto out;
-       err = fill_ipv4_sockaddr(hostname, &saddr);
-       free(hostname);
-       if (!err)
-               goto out;
+               return retval;
+       if (!fill_ipv4_sockaddr(hostname, &saddr))
+               goto fail;
 
        options = po_split(*extra_opts);
        if (!options) {
                nfs_error(_("%s: internal option parsing error"), progname);
 
        options = po_split(*extra_opts);
        if (!options) {
                nfs_error(_("%s: internal option parsing error"), progname);
-               goto out;
+               goto fail;
        }
 
        }
 
-       if (!set_mandatory_options(type, &saddr, options))
-               goto out;
-
-       if (try_mount(spec, node, type, flags, options, fake, extra_opts)) {
-               mount_error(spec, node, errno);
+       if (!validate_options(type, &saddr, options, fake))
                goto out;
                goto out;
-       }
 
 
-       retval = EX_SUCCESS;
+       if (po_rightmost(options, "bg", "fg") == PO_KEY1_RIGHTMOST)
+               retval = nfsmount_bg(spec, node, type, hostname, flags,
+                                       options, fake, child, extra_opts);
+       else
+               retval = nfsmount_fg(spec, node, type, flags, options,
+                                                       fake, extra_opts);
 
 out:
        po_destroy(options);
 
 out:
        po_destroy(options);
+fail:
+       free(hostname);
        return retval;
 }
        return retval;
 }