diff options
Diffstat (limited to 'nfs/main.c')
-rw-r--r-- | nfs/main.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/nfs/main.c b/nfs/main.c new file mode 100644 index 00000000..92a49861 --- /dev/null +++ b/nfs/main.c @@ -0,0 +1,403 @@ +/* + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <hurd/netfs.h> +#include <sys/socket.h> +#include <stdio.h> +#include <device/device.h> +#include "nfs.h" +#include <netinet/in.h> +#include <unistd.h> +#include <string.h> +#include <maptime.h> +#include <argp.h> +#include <argz.h> +#include <error.h> + +extern char *localhost (); + +/* Default number of times to retry RPCs when mounted soft. */ +#define DEFAULT_SOFT_RETRIES 3 + +/* Default number of seconds to timeout cached stat information. */ +#define DEFAULT_STAT_TIMEOUT 3 + +/* Default number of seconds to timeout cached file contents. */ +#define DEFAULT_CACHE_TIMEOUT 3 + +/* Default number of seconds to timeout cache positive dir hits. */ +#define DEFAULT_NAME_CACHE_TIMEOUT 3 + +/* Default number of seconds to timeout cache negative dir hits. */ +#define DEFAULT_NAME_CACHE_NEG_TIMEOUT 3 + +/* Default maximum number of bytes to read at once. */ +#define DEFAULT_READ_SIZE 8192 + +/* Default maximum number of bytes to write at once. */ +#define DEFAULT_WRITE_SIZE 8192 + + +/* Number of seconds to timeout cached stat information. */ +int stat_timeout = DEFAULT_STAT_TIMEOUT; + +/* Number of seconds to timeout cached file contents. */ +int cache_timeout = DEFAULT_CACHE_TIMEOUT; + +/* Number of seconds to timeout cached positive dir hits. */ +int name_cache_timeout = DEFAULT_NAME_CACHE_TIMEOUT; + +/* Number of seconds to timeout cached negative dir hits. */ +int name_cache_neg_timeout = DEFAULT_NAME_CACHE_NEG_TIMEOUT; + +/* Number of seconds to wait for first retransmission of an RPC. */ +int initial_transmit_timeout = 1; + +/* Maximum number of seconds to wait between retransmission of RPCs. */ +int max_transmit_timeout = 30; + +/* Maximum number of retries to send when mounted soft. */ +int soft_retries = DEFAULT_SOFT_RETRIES; + +/* True iff we are mounted soft. */ +int mounted_soft = 0; + +/* Maximum number of bytes to read at once. */ +int read_size = DEFAULT_READ_SIZE; + +/* Maximum number of bytes to write at once. */ +int write_size = DEFAULT_WRITE_SIZE; + +#define OPT_SOFT 's' +#define OPT_HARD 'h' +#define OPT_RSIZE 'R' +#define OPT_WSIZE 'W' +#define OPT_STAT_TO -2 +#define OPT_CACHE_TO -3 +#define OPT_INIT_TR_TO -4 +#define OPT_MAX_TR_TO -5 +#define OPT_MNT_PORT -6 +#define OPT_MNT_PORT_D -7 +#define OPT_NFS_PORT -8 +#define OPT_NFS_PORT_D -9 +#define OPT_HOLD -10 +#define OPT_MNT_PROG -11 +#define OPT_NFS_PROG -12 +#define OPT_PMAP_PORT -13 +#define OPT_NCACHE_TO -14 +#define OPT_NCACHE_NEG_TO -15 + +/* Return a string corresponding to the printed rep of DEFAULT_what */ +#define ___D(what) #what +#define __D(what) ___D(what) +#define _D(what) __D(DEFAULT_ ## what) + +/* Options usable both at startup and at runtime. */ +static const struct argp_option common_options[] = +{ + {0,0,0,0,0,1}, + {"soft", OPT_SOFT, "RETRIES", OPTION_ARG_OPTIONAL, + "File system requests will eventually fail, after RETRIES tries if" + " specified, otherwise " _D(SOFT_RETRIES)}, + {"hard", OPT_HARD, 0, 0, + "Retry file systems requests until they succeed"}, + + {0,0,0,0,0,2}, + {"read-size", OPT_RSIZE, "BYTES", 0, + "Max packet size for reads (default " _D(READ_SIZE) ")"}, + {"rsize",0,0,OPTION_ALIAS}, + {"write-size", OPT_WSIZE, "BYTES", 0, + "Max packet size for writes (default " _D(WRITE_SIZE)")"}, + {"wsize",0,0,OPTION_ALIAS}, + + {0,0,0,0,"Timeouts:",3}, + {"stat-timeout", OPT_STAT_TO, "SEC", 0, + "Timeout for cached stat information (default " _D(STAT_TIMEOUT) ")"}, + {"cache-timeout", OPT_CACHE_TO, "SEC", 0, + "Timeout for cached file data (default " _D(CACHE_TIMEOUT) ")"}, + {"name-cache-timeout", OPT_NCACHE_TO, "SEC", 0, + "Timeout for positive directory cache entries (default " + _D(NAME_CACHE_TIMEOUT) ")"}, + {"name-cache-neg-timeout", OPT_NCACHE_NEG_TO, "SEC", 0, + "Timeout for negative directory cache entires (default " + _D(NAME_CACHE_NEG_TIMEOUT) ")"}, + {"init-transmit-timeout", OPT_INIT_TR_TO,"SEC", 0}, + {"max-transmit-timeout", OPT_MAX_TR_TO, "SEC", 0}, + + {0} +}; + +static error_t +parse_common_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case OPT_SOFT: + mounted_soft = 1; + if (arg) + soft_retries = atoi (arg); + break; + case OPT_HARD: + mounted_soft = 0; + break; + + case OPT_RSIZE: read_size = atoi (arg); break; + case OPT_WSIZE: write_size = atoi (arg); break; + + case OPT_STAT_TO: stat_timeout = atoi (arg); break; + case OPT_CACHE_TO: cache_timeout = atoi (arg); break; + case OPT_INIT_TR_TO: initial_transmit_timeout = atoi (arg); break; + case OPT_MAX_TR_TO: max_transmit_timeout = atoi (arg); break; + case OPT_NCACHE_TO: name_cache_timeout = atoi (arg); break; + case OPT_NCACHE_NEG_TO: name_cache_neg_timeout = atoi (arg); break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +/* Options usable only at startup. */ +static const struct argp_option startup_options[] = { + {0,0,0,0,"Server specification:",10}, + {"mount-port", OPT_MNT_PORT, "PORT", 0, + "Port for mount server"}, + {"default-mount-port", OPT_MNT_PORT_D,"PORT", 0, + "Port for mount server, if none can be found automatically"}, + {"mount-program", OPT_MNT_PROG, "ID[.VERS]"}, + + {"nfs-port", OPT_NFS_PORT, "PORT", 0, + "Port for nfs operations"}, + {"default-nfs-port", OPT_NFS_PORT_D,"PORT", 0, + "Port for nfs operations, if none can be found automatically"}, + {"nfs-program", OPT_NFS_PROG, "ID[.VERS]"}, + + {"pmap-port", OPT_PMAP_PORT, "SVC|PORT"}, + + {"hold", OPT_HOLD, 0, OPTION_HIDDEN}, /* */ + { 0 } +}; +static char *args_doc = "REMOTE_FS [HOST]"; +static char *doc = "Hurd nfs translator" +"\vIf HOST is not specified, an attempt is made to extract" +" it from REMOTE_FS, using either the `HOST:FS' or `FS@HOST' notations."; + +static const struct argp_child +runtime_argp_children[] = { {&netfs_std_runtime_argp}, {0} }; +static struct argp +runtime_argp = { common_options, parse_common_opt, 0, 0, + runtime_argp_children }; + +/* Use by netfs_set_options to handle runtime option parsing. */ +struct argp *netfs_runtime_argp = &runtime_argp; + +/* Where to find the remote filesystem. */ +static char *remote_fs = 0; +static char *host = 0; + +/* Return an argz string describing the current options. Fill *ARGZ + with a pointer to newly malloced storage holding the list and *LEN + to the length of that storage. */ +error_t +netfs_append_args (char **argz, size_t *argz_len) +{ + char buf[80]; + error_t err = 0; + +#define FOPT(fmt, arg) \ + do { \ + if (! err) \ + { \ + snprintf (buf, sizeof buf, fmt, arg); \ + err = argz_add (argz, argz_len, buf); \ + } \ + } while (0) + + if (mounted_soft) + FOPT ("--soft=%d", soft_retries); + else + err = argz_add (argz, argz_len, "--hard"); + + FOPT ("--read-size=%d", read_size); + FOPT ("--write-size=%d", write_size); + + FOPT ("--stat-timeout=%d", stat_timeout); + FOPT ("--cache-timeout=%d", cache_timeout); + FOPT ("--init-transmit-timeout=%d", initial_transmit_timeout); + FOPT ("--max-transmit-timeout=%d", max_transmit_timeout); + FOPT ("--name-cache-timeout=%d", name_cache_timeout); + FOPT ("--name-cache-neg-timeout=%d", name_cache_neg_timeout); + + if (! err) + err = netfs_append_std_options (argz, argz_len); + + if (! err) + { + char *fs; + if (asprintf (&fs, "%s:%s", host, remote_fs)) + { + err = argz_add (argz, argz_len, fs); + free (fs); + } + else + err = ENOMEM; + } + + return err; +} + +/* Extract the host and remote filesystem names from SPEC, which should use + either HOST:FS or FS@HOST notation. Returns the malloced storage into + which both REMOTE_FS and HOST point, or 0 if SPEC is invalid. */ +static char * +extract_nfs_args (char *spec, char **remote_fs, char **host) +{ + char *sep; + + spec = strdup (spec); /* So we can trash it. */ + + sep = index (spec, ':'); + if (sep) + { + *sep++ = '\0'; + *host = spec; + *remote_fs = sep; + return spec; + } + + sep = index (spec, '@'); + if (sep) + { + *sep++ = '\0'; + *host = sep; + *remote_fs = spec; + return spec; + } + + free (spec); + + return 0; +} + +static error_t +parse_startup_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case OPT_MNT_PORT: + mount_port_override = 1; + /* fall through */ + case OPT_MNT_PORT_D: + mount_port = atoi (arg); + break; + + case OPT_NFS_PORT: + nfs_port_override = 1; + /* fall through */ + case OPT_NFS_PORT_D: + nfs_port = atoi (arg); + break; + + case ARGP_KEY_ARG: + if (state->arg_num == 0) + remote_fs = arg; + else if (state->arg_num == 1) + host = arg; + else + return ARGP_ERR_UNKNOWN; + break; + + case ARGP_KEY_END: + if (!host && !extract_nfs_args (remote_fs, &remote_fs, &host)) + argp_error (state, "No HOST specified"); + break; + + case ARGP_KEY_NO_ARGS: + argp_error (state, "No REMOTE_FS specified"); + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* NFS client main program */ +int +main (int argc, char **argv) +{ + struct argp common_argp = { common_options, parse_common_opt }; + const struct argp_child argp_children[] = + { {&common_argp}, {&netfs_std_startup_argp}, {0} }; + struct argp argp = + { startup_options, parse_startup_opt, args_doc, doc, argp_children }; + mach_port_t bootstrap; + struct sockaddr_in addr; + int ret; + + argp_parse (&argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + netfs_init (); + + main_udp_socket = socket (PF_INET, SOCK_DGRAM, 0); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons (IPPORT_RESERVED); + do + { + addr.sin_port = htons (ntohs (addr.sin_port) - 1); + ret = bind (main_udp_socket, (struct sockaddr *)&addr, + sizeof (struct sockaddr_in)); + if (ret == -1 && errno == EACCES) + { + /* We aren't allowed privileged ports; no matter; + let the server deny us later if it wants. */ + ret = 0; + break; + } + } + while ((ret == -1) && (errno == EADDRINUSE)); + if (ret == -1) + { + perror ("binding main udp socket"); + exit (1); + } + + errno = maptime_map (0, 0, &mapped_time); + if (errno) + error (2, errno, "mapping time"); + + cthread_detach (cthread_fork ((cthread_fn_t) timeout_service_thread, 0)); + cthread_detach (cthread_fork ((cthread_fn_t) rpc_receive_thread, 0)); + + hostname = localhost (); + + netfs_root_node = mount_root (remote_fs, host); + + if (!netfs_root_node) + exit (1); + + netfs_startup (bootstrap, 0); + + for (;;) + netfs_server_loop (); +} + |