diff options
Diffstat (limited to 'mount/nfsmount.c')
-rw-r--r-- | mount/nfsmount.c | 246 |
1 files changed, 183 insertions, 63 deletions
diff --git a/mount/nfsmount.c b/mount/nfsmount.c index ee869dc44..fd9364669 100644 --- a/mount/nfsmount.c +++ b/mount/nfsmount.c @@ -17,6 +17,9 @@ * * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>: * Omit the call to connect() for Linux version 1.3.11 or later. + * + * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com> + * Implemented the "bg", "fg" and "retry" mount options for NFS. */ /* @@ -34,17 +37,14 @@ #include <sys/socket.h> #include <sys/time.h> #include <sys/utsname.h> +#include <sys/stat.h> #include <arpa/inet.h> #include "sundries.h" #include "nfsmount.h" -#if defined(__GLIBC__) -#define _LINUX_SOCKET_H -#endif /* __GLIBC__ */ -#define _I386_BITOPS_H -#include <linux/fs.h> #include <linux/nfs.h> +#include "mount_constants.h" #include "nfs_mount3.h" static char *nfs_strerror(int stat); @@ -96,8 +96,9 @@ find_kernel_nfs_mount_version(void) { } int nfsmount(const char *spec, const char *node, int *flags, - char **extra_opts, char **mount_opts) + char **extra_opts, char **mount_opts, int running_bg) { + static char *prev_bg_host; char hostdir[1024]; CLIENT *mclient; char *hostname; @@ -115,8 +116,9 @@ int nfsmount(const char *spec, const char *node, int *flags, struct sockaddr_in server_addr; struct sockaddr_in mount_server_addr; int msock, fsock; - struct timeval pertry_timeout; + struct timeval retry_timeout; struct fhstatus status; + struct stat statbuf; char *s; int port; int mountport; @@ -133,9 +135,14 @@ int nfsmount(const char *spec, const char *node, int *flags, int mountvers; int nfsprog; int nfsvers; + int retval; + time_t t; + time_t prevt; + time_t timeout; find_kernel_nfs_mount_version(); + retval = EX_FAIL; msock = fsock = -1; mclient = NULL; if (strlen(spec) >= sizeof(hostdir)) { @@ -148,21 +155,30 @@ int nfsmount(const char *spec, const char *node, int *flags, hostname = hostdir; dirname = s + 1; *s = '\0'; - } - else { + } else { fprintf(stderr, "mount: " "directory to mount not in host:dir format\n"); goto fail; } server_addr.sin_family = AF_INET; - if (!inet_aton(hostname, &server_addr.sin_addr)) { +#if 1 /* old libc's do not have inet_aton() -- change 1 to 0 */ + if (!inet_aton(hostname, &server_addr.sin_addr)) +#endif + { if ((hp = gethostbyname(hostname)) == NULL) { fprintf(stderr, "mount: can't get address for %s\n", hostname); goto fail; - } else - memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length); + } else { + if (hp->h_length > sizeof(struct in_addr)) { + fprintf(stderr, + "mount: got bad hp->h_length\n"); + hp->h_length = sizeof(struct in_addr); + } + memcpy(&server_addr.sin_addr, + hp->h_addr, hp->h_length); + } } memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr)); @@ -203,7 +219,7 @@ int nfsmount(const char *spec, const char *node, int *flags, nocto = 0; nolock = 0; noac = 0; - retry = 10000; + retry = 10000; /* 10000 minutes ~ 1 week */ tcp = 0; mountprog = MOUNTPROG; @@ -306,9 +322,11 @@ int nfsmount(const char *spec, const char *node, int *flags, else printf("Warning: option nolock is not supported.\n"); } else { - printf("unknown nfs mount option: " - "%s%s\n", val ? "" : "no", opt); - goto fail; + if (!sloppy) { + printf("unknown nfs mount option: " + "%s%s\n", val ? "" : "no", opt); + goto fail; + } } } } @@ -349,9 +367,6 @@ int nfsmount(const char *spec, const char *node, int *flags, printf("tcp = %d\n", (data.flags & NFS_MOUNT_TCP) != 0); #endif -#if 0 - goto fail; -#endif #endif data.version = nfs_mount_version; @@ -360,58 +375,132 @@ int nfsmount(const char *spec, const char *node, int *flags, if (*flags & MS_REMOUNT) return 0; + /* + * If the previous mount operation on the same host was + * backgrounded, and the "bg" for this mount is also set, + * give up immediately, to avoid the initial timeout. + */ + if (bg && !running_bg && + prev_bg_host && strcmp(hostname, prev_bg_host) == 0) { + if (retry > 0) + retval = EX_BG; + return retval; + } + /* create mount deamon client */ /* See if the nfs host = mount host. */ if (mounthost) { if (mounthost[0] >= '0' && mounthost[0] <= '9') { mount_server_addr.sin_family = AF_INET; mount_server_addr.sin_addr.s_addr = inet_addr(hostname); - } - else if ((hp = gethostbyname(mounthost)) == NULL) { - fprintf(stderr, "mount: can't get address for %s\n", hostname); - goto fail; - } - else { - mount_server_addr.sin_family = AF_INET; - memcpy(&mount_server_addr.sin_addr, hp->h_addr, hp->h_length); + } else { + if ((hp = gethostbyname(mounthost)) == NULL) { + fprintf(stderr, "mount: can't get address for %s\n", + hostname); + goto fail; + } else { + if (hp->h_length > sizeof(struct in_addr)) { + fprintf(stderr, + "mount: got bad hp->h_length?\n"); + hp->h_length = sizeof(struct in_addr); + } + mount_server_addr.sin_family = AF_INET; + memcpy(&mount_server_addr.sin_addr, + hp->h_addr, hp->h_length); + } } } - mount_server_addr.sin_port = htons(mountport); - msock = RPC_ANYSOCK; - if ((mclient = clnttcp_create(&mount_server_addr, - mountprog, mountvers, &msock, 0, 0)) == NULL) { - mount_server_addr.sin_port = htons(mountport); - msock = RPC_ANYSOCK; - pertry_timeout.tv_sec = 3; - pertry_timeout.tv_usec = 0; - if ((mclient = clntudp_create(&mount_server_addr, - mountprog, mountvers, pertry_timeout, &msock)) == NULL) { - clnt_pcreateerror("mount clntudp_create"); + /* + * The following loop implements the mount retries. On the first + * call, "running_bg" is 0. When the mount times out, and the + * "bg" option is set, the exit status EX_BG will be returned. + * For a backgrounded mount, there will be a second call by the + * child process with "running_bg" set to 1. + * + * The case where the mount point is not present and the "bg" + * option is set, is treated as a timeout. This is done to + * support nested mounts. + * + * The "retry" count specified by the user is the number of + * minutes to retry before giving up. + * + * Only the first error message will be displayed. + */ + retry_timeout.tv_sec = 3; + retry_timeout.tv_usec = 0; + total_timeout.tv_sec = 20; + total_timeout.tv_usec = 0; + timeout = time(NULL) + 60 * retry; + prevt = 0; + t = 30; + val = 1; + for (;;) { + if (bg && stat(node, &statbuf) == -1) { + if (running_bg) { + sleep(val); /* 1, 2, 4, 8, 16, 30, ... */ + val *= 2; + if (val > 30) + val = 30; + } + } else { + /* be careful not to use too many CPU cycles */ + if (t - prevt < 30) + sleep(30); + + /* contact the mount daemon via TCP */ + mount_server_addr.sin_port = htons(mountport); + msock = RPC_ANYSOCK; + mclient = clnttcp_create(&mount_server_addr, + mountprog, mountvers, + &msock, 0, 0); + + /* if this fails, contact the mount daemon via UDP */ + if (!mclient) { + mount_server_addr.sin_port = htons(mountport); + msock = RPC_ANYSOCK; + mclient = clntudp_create(&mount_server_addr, + mountprog, mountvers, + retry_timeout, &msock); + } + if (mclient) { + /* try to mount hostname:dirname */ + mclient->cl_auth = authunix_create_default(); + clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, + (xdrproc_t) xdr_dirpath, (caddr_t) &dirname, + (xdrproc_t) xdr_fhstatus, (caddr_t) &status, + total_timeout); + if (clnt_stat == RPC_SUCCESS) + break; /* we're done */ + if (errno != ECONNREFUSED) { + clnt_perror(mclient, "mount"); + goto fail; /* don't retry */ + } + if (!running_bg && prevt == 0) + clnt_perror(mclient, "mount"); + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + mclient = 0; + close(msock); + } else { + if (!running_bg && prevt == 0) + clnt_pcreateerror("mount"); + } + prevt = t; + } + if (!bg) + goto fail; + if (!running_bg) { + prev_bg_host = xstrdup(hostname); + if (retry > 0) + retval = EX_BG; goto fail; } -#ifdef NFS_MOUNT_DEBUG - printf("using UDP for mount deamon\n"); -#endif + t = time(NULL); + if (t >= timeout) + goto fail; } -#ifdef NFS_MOUNT_DEBUG - else - printf("using TCP for mount deamon\n"); -#endif - mclient->cl_auth = authunix_create_default(); - total_timeout.tv_sec = 20; - total_timeout.tv_usec = 0; - /* try to mount hostname:dirname */ - - clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, - (xdrproc_t) xdr_dirpath, &dirname, - (xdrproc_t) xdr_fhstatus, &status, - total_timeout); - if (clnt_stat != RPC_SUCCESS) { - clnt_perror(mclient, "rpc mount"); - goto fail; - } if (status.fhs_status != 0) { fprintf(stderr, "mount: %s:%s failed, reason given by server: %s\n", @@ -485,15 +574,16 @@ int nfsmount(const char *spec, const char *node, int *flags, fail: if (msock != -1) { - auth_destroy(mclient->cl_auth); - clnt_destroy(mclient); + if (mclient) { + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + } close(msock); } if (fsock != -1) close(fsock); - return 1; -} - + return retval; +} /* * We need to translate between nfs status return values and @@ -554,3 +644,33 @@ static char *nfs_strerror(int stat) return buf; } +#if 0 +int +my_getport(struct in_addr server, struct timeval *timeo, ...) +{ + struct sockaddr_in sin; + struct pmap pmap; + CLIENT *clnt; + int sock = RPC_ANYSOCK, port; + + pmap.pm_prog = prog; + pmap.pm_vers = vers; + pmap.pm_prot = prot; + pmap.pm_port = 0; + sin.sin_family = AF_INET; + sin.sin_addr = server; + sin.sin_port = htons(111); + clnt = clntudp_create(&sin, 100000, 2, *timeo, &sock); + status = clnt_call(clnt, PMAP_GETPORT, + &pmap, (xdrproc_t) xdr_pmap, + &port, (xdrproc_t) xdr_uint); + if (status != SUCCESS) { + /* natter */ + port = 0; + } + + clnt_destroy(clnt); + close(sock); + return port; +} +#endif |