3602 lines
98 KiB
Diff
3602 lines
98 KiB
Diff
|
diff --git a/configure.ac b/configure.ac
|
|||
|
index e0ca70e..1b653e4 100644
|
|||
|
--- a/configure.ac
|
|||
|
+++ b/configure.ac
|
|||
|
@@ -136,6 +136,31 @@ AC_ARG_ENABLE(ipv6,
|
|||
|
AC_SUBST(enable_ipv6)
|
|||
|
AM_CONDITIONAL(CONFIG_IPV6, [test "$enable_ipv6" = "yes"])
|
|||
|
|
|||
|
+if test "$enable_mount" = yes; then
|
|||
|
+ AC_ARG_ENABLE(mountconfig,
|
|||
|
+ [AC_HELP_STRING([--enable-mountconfig],
|
|||
|
+ [enable mount to use a configuration file])],
|
|||
|
+ mountconfig=$enableval,
|
|||
|
+ mountconfig=no)
|
|||
|
+ if test "$enable_mountconfig" = yes; then
|
|||
|
+ AC_DEFINE(MOUNT_CONFIG, 1,
|
|||
|
+ [Define this if you want mount to read a configuration file])
|
|||
|
+ AC_ARG_WITH(mountfile,
|
|||
|
+ [AC_HELP_STRING([--with-mountfile=filename],
|
|||
|
+ [Using filename as the NFS mount options file [/etc/nfsmounts.conf]]
|
|||
|
+ )],
|
|||
|
+ mountfile=$withval,
|
|||
|
+ mountfile=/etc/nfsmount.conf)
|
|||
|
+ AC_SUBST(mountfile)
|
|||
|
+ AC_DEFINE_UNQUOTED(MOUNTOPTS_CONFFILE, "$mountfile",
|
|||
|
+ [This defines the location of the NFS mount configuration file])
|
|||
|
+ else
|
|||
|
+ enable_mountconfig=
|
|||
|
+ fi
|
|||
|
+ AC_SUBST(enable_mountconfig)
|
|||
|
+ AM_CONDITIONAL(MOUNT_CONFIG, [test "$enable_mountconfig" = "yes"])
|
|||
|
+fi
|
|||
|
+
|
|||
|
dnl Check for TI-RPC library and headers
|
|||
|
AC_LIBTIRPC
|
|||
|
|
|||
|
diff --git a/support/include/Makefile.am b/support/include/Makefile.am
|
|||
|
index 718abda..f5a77ec 100644
|
|||
|
--- a/support/include/Makefile.am
|
|||
|
+++ b/support/include/Makefile.am
|
|||
|
@@ -14,6 +14,7 @@ noinst_HEADERS = \
|
|||
|
xio.h \
|
|||
|
xlog.h \
|
|||
|
xmalloc.h \
|
|||
|
- xcommon.h
|
|||
|
+ xcommon.h \
|
|||
|
+ conffile.h
|
|||
|
|
|||
|
MAINTAINERCLEANFILES = Makefile.in
|
|||
|
diff --git a/support/include/conffile.h b/support/include/conffile.h
|
|||
|
new file mode 100644
|
|||
|
index 0000000..132a149
|
|||
|
--- /dev/null
|
|||
|
+++ b/support/include/conffile.h
|
|||
|
@@ -0,0 +1,77 @@
|
|||
|
+/* $OpenBSD: conf.h,v 1.30 2004/06/25 20:25:34 hshoexer Exp $ */
|
|||
|
+/* $EOM: conf.h,v 1.13 2000/09/18 00:01:47 ho Exp $ */
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist. All rights reserved.
|
|||
|
+ * Copyright (c) 2000, 2003 H<>kan Olsson. All rights reserved.
|
|||
|
+ *
|
|||
|
+ * Redistribution and use in source and binary forms, with or without
|
|||
|
+ * modification, are permitted provided that the following conditions
|
|||
|
+ * are met:
|
|||
|
+ * 1. Redistributions of source code must retain the above copyright
|
|||
|
+ * notice, this list of conditions and the following disclaimer.
|
|||
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|||
|
+ * notice, this list of conditions and the following disclaimer in the
|
|||
|
+ * documentation and/or other materials provided with the distribution.
|
|||
|
+ *
|
|||
|
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|||
|
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|||
|
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|||
|
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|||
|
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|||
|
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
|
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
|
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|||
|
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
+ */
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * This code was written under funding by Ericsson Radio Systems.
|
|||
|
+ */
|
|||
|
+
|
|||
|
+#ifndef _CONFFILE_H_
|
|||
|
+#define _CONFFILE_H_
|
|||
|
+
|
|||
|
+#include <sys/queue.h>
|
|||
|
+
|
|||
|
+struct conf_list_node {
|
|||
|
+ TAILQ_ENTRY(conf_list_node) link;
|
|||
|
+ char *field;
|
|||
|
+};
|
|||
|
+
|
|||
|
+struct conf_list {
|
|||
|
+ size_t cnt;
|
|||
|
+ TAILQ_HEAD(conf_list_fields_head, conf_list_node) fields;
|
|||
|
+};
|
|||
|
+
|
|||
|
+extern char *conf_path;
|
|||
|
+
|
|||
|
+extern int conf_begin(void);
|
|||
|
+extern int conf_decode_base64(u_int8_t *, u_int32_t *, u_char *);
|
|||
|
+extern int conf_end(int, int);
|
|||
|
+extern void conf_free_list(struct conf_list *);
|
|||
|
+extern struct sockaddr *conf_get_address(char *, char *);
|
|||
|
+extern struct conf_list *conf_get_list(char *, char *);
|
|||
|
+extern struct conf_list *conf_get_tag_list(char *);
|
|||
|
+extern int conf_get_num(char *, char *, int);
|
|||
|
+extern char *conf_get_str(char *, char *);
|
|||
|
+extern char *conf_get_section(char *, char *, char *);
|
|||
|
+extern void conf_init(void);
|
|||
|
+extern int conf_match_num(char *, char *, int);
|
|||
|
+extern void conf_reinit(void);
|
|||
|
+extern int conf_remove(int, char *, char *);
|
|||
|
+extern int conf_remove_section(int, char *);
|
|||
|
+extern void conf_report(void);
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Convert letter from upper case to lower case
|
|||
|
+ */
|
|||
|
+static inline void upper2lower(char *str)
|
|||
|
+{
|
|||
|
+ char c;
|
|||
|
+
|
|||
|
+ while ((c = tolower(*str)))
|
|||
|
+ *str++ = c;
|
|||
|
+}
|
|||
|
+#endif /* _CONFFILE_H_ */
|
|||
|
diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
|
|||
|
index 096f56d..e9462fc 100644
|
|||
|
--- a/support/nfs/Makefile.am
|
|||
|
+++ b/support/nfs/Makefile.am
|
|||
|
@@ -4,7 +4,7 @@ noinst_LIBRARIES = libnfs.a
|
|||
|
libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
|
|||
|
xlog.c xcommon.c wildmat.c nfsclient.c \
|
|||
|
nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \
|
|||
|
- svc_socket.c cacheio.c closeall.c nfs_mntent.c
|
|||
|
+ svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c
|
|||
|
|
|||
|
MAINTAINERCLEANFILES = Makefile.in
|
|||
|
|
|||
|
diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
|
|||
|
new file mode 100644
|
|||
|
index 0000000..b277c2a
|
|||
|
--- /dev/null
|
|||
|
+++ b/support/nfs/conffile.c
|
|||
|
@@ -0,0 +1,962 @@
|
|||
|
+/* $OpenBSD: conf.c,v 1.55 2003/06/03 14:28:16 ho Exp $ */
|
|||
|
+/* $EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $ */
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
|
|||
|
+ * Copyright (c) 2000, 2001, 2002 H<>kan Olsson. All rights reserved.
|
|||
|
+ *
|
|||
|
+ * Redistribution and use in source and binary forms, with or without
|
|||
|
+ * modification, are permitted provided that the following conditions
|
|||
|
+ * are met:
|
|||
|
+ * 1. Redistributions of source code must retain the above copyright
|
|||
|
+ * notice, this list of conditions and the following disclaimer.
|
|||
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|||
|
+ * notice, this list of conditions and the following disclaimer in the
|
|||
|
+ * documentation and/or other materials provided with the distribution.
|
|||
|
+ *
|
|||
|
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|||
|
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|||
|
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|||
|
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|||
|
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|||
|
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
|
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
|
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|||
|
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
+ */
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * This code was written under funding by Ericsson Radio Systems.
|
|||
|
+ */
|
|||
|
+
|
|||
|
+#include <sys/param.h>
|
|||
|
+#include <sys/mman.h>
|
|||
|
+#include <sys/socket.h>
|
|||
|
+#include <sys/stat.h>
|
|||
|
+#include <netinet/in.h>
|
|||
|
+#include <arpa/inet.h>
|
|||
|
+#include <ctype.h>
|
|||
|
+#include <fcntl.h>
|
|||
|
+#include <stdio.h>
|
|||
|
+#include <stdlib.h>
|
|||
|
+#include <string.h>
|
|||
|
+#include <unistd.h>
|
|||
|
+#include <errno.h>
|
|||
|
+#include <err.h>
|
|||
|
+#include <syslog.h>
|
|||
|
+
|
|||
|
+#include "conffile.h"
|
|||
|
+#include "xlog.h"
|
|||
|
+
|
|||
|
+static void conf_load_defaults (int);
|
|||
|
+static int conf_set(int , char *, char *, char *,
|
|||
|
+ char *, int , int );
|
|||
|
+
|
|||
|
+struct conf_trans {
|
|||
|
+ TAILQ_ENTRY (conf_trans) link;
|
|||
|
+ int trans;
|
|||
|
+ enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op;
|
|||
|
+ char *section;
|
|||
|
+ char *arg;
|
|||
|
+ char *tag;
|
|||
|
+ char *value;
|
|||
|
+ int override;
|
|||
|
+ int is_default;
|
|||
|
+};
|
|||
|
+
|
|||
|
+TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue;
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Radix-64 Encoding.
|
|||
|
+ */
|
|||
|
+static const u_int8_t bin2asc[]
|
|||
|
+ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|||
|
+
|
|||
|
+static const u_int8_t asc2bin[] =
|
|||
|
+{
|
|||
|
+ 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
+ 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
+ 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
+ 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
+ 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
+ 255, 255, 255, 62, 255, 255, 255, 63,
|
|||
|
+ 52, 53, 54, 55, 56, 57, 58, 59,
|
|||
|
+ 60, 61, 255, 255, 255, 255, 255, 255,
|
|||
|
+ 255, 0, 1, 2, 3, 4, 5, 6,
|
|||
|
+ 7, 8, 9, 10, 11, 12, 13, 14,
|
|||
|
+ 15, 16, 17, 18, 19, 20, 21, 22,
|
|||
|
+ 23, 24, 25, 255, 255, 255, 255, 255,
|
|||
|
+ 255, 26, 27, 28, 29, 30, 31, 32,
|
|||
|
+ 33, 34, 35, 36, 37, 38, 39, 40,
|
|||
|
+ 41, 42, 43, 44, 45, 46, 47, 48,
|
|||
|
+ 49, 50, 51, 255, 255, 255, 255, 255
|
|||
|
+};
|
|||
|
+
|
|||
|
+struct conf_binding {
|
|||
|
+ LIST_ENTRY (conf_binding) link;
|
|||
|
+ char *section;
|
|||
|
+ char *arg;
|
|||
|
+ char *tag;
|
|||
|
+ char *value;
|
|||
|
+ int is_default;
|
|||
|
+};
|
|||
|
+
|
|||
|
+char *conf_path;
|
|||
|
+LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256];
|
|||
|
+
|
|||
|
+static char *conf_addr;
|
|||
|
+
|
|||
|
+static __inline__ u_int8_t
|
|||
|
+conf_hash(char *s)
|
|||
|
+{
|
|||
|
+ u_int8_t hash = 0;
|
|||
|
+
|
|||
|
+ while (*s) {
|
|||
|
+ hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s);
|
|||
|
+ s++;
|
|||
|
+ }
|
|||
|
+ return hash;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Insert a tag-value combination from LINE (the equal sign is at POS)
|
|||
|
+ */
|
|||
|
+static int
|
|||
|
+conf_remove_now(char *section, char *tag)
|
|||
|
+{
|
|||
|
+ struct conf_binding *cb, *next;
|
|||
|
+
|
|||
|
+ cb = LIST_FIRST(&conf_bindings[conf_hash (section)]);
|
|||
|
+ for (; cb; cb = next) {
|
|||
|
+ next = LIST_NEXT(cb, link);
|
|||
|
+ if (strcasecmp(cb->section, section) == 0
|
|||
|
+ && strcasecmp(cb->tag, tag) == 0) {
|
|||
|
+ LIST_REMOVE(cb, link);
|
|||
|
+ xlog(LOG_INFO,"[%s]:%s->%s removed", section, tag, cb->value);
|
|||
|
+ free(cb->section);
|
|||
|
+ free(cb->arg);
|
|||
|
+ free(cb->tag);
|
|||
|
+ free(cb->value);
|
|||
|
+ free(cb);
|
|||
|
+ return 0;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ return 1;
|
|||
|
+}
|
|||
|
+
|
|||
|
+static int
|
|||
|
+conf_remove_section_now(char *section)
|
|||
|
+{
|
|||
|
+ struct conf_binding *cb, *next;
|
|||
|
+ int unseen = 1;
|
|||
|
+
|
|||
|
+ cb = LIST_FIRST(&conf_bindings[conf_hash (section)]);
|
|||
|
+ for (; cb; cb = next) {
|
|||
|
+ next = LIST_NEXT(cb, link);
|
|||
|
+ if (strcasecmp(cb->section, section) == 0) {
|
|||
|
+ unseen = 0;
|
|||
|
+ LIST_REMOVE(cb, link);
|
|||
|
+ xlog(LOG_INFO, "[%s]:%s->%s removed", section, cb->tag, cb->value);
|
|||
|
+ free(cb->section);
|
|||
|
+ free(cb->arg);
|
|||
|
+ free(cb->tag);
|
|||
|
+ free(cb->value);
|
|||
|
+ free(cb);
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ return unseen;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Insert a tag-value combination from LINE (the equal sign is at POS)
|
|||
|
+ * into SECTION of our configuration database.
|
|||
|
+ */
|
|||
|
+static int
|
|||
|
+conf_set_now(char *section, char *arg, char *tag,
|
|||
|
+ char *value, int override, int is_default)
|
|||
|
+{
|
|||
|
+ struct conf_binding *node = 0;
|
|||
|
+
|
|||
|
+ if (override)
|
|||
|
+ conf_remove_now(section, tag);
|
|||
|
+ else if (conf_get_section(section, arg, tag)) {
|
|||
|
+ if (!is_default) {
|
|||
|
+ xlog(LOG_INFO, "conf_set: duplicate tag [%s]:%s, ignoring...\n",
|
|||
|
+ section, tag);
|
|||
|
+ }
|
|||
|
+ return 1;
|
|||
|
+ }
|
|||
|
+ node = calloc(1, sizeof *node);
|
|||
|
+ if (!node) {
|
|||
|
+ xlog_warn("conf_set: calloc (1, %lu) failed", (unsigned long)sizeof *node);
|
|||
|
+ return 1;
|
|||
|
+ }
|
|||
|
+ node->section = strdup(section);
|
|||
|
+ if (arg)
|
|||
|
+ node->arg = strdup(arg);
|
|||
|
+ node->tag = strdup(tag);
|
|||
|
+ node->value = strdup(value);
|
|||
|
+ node->is_default = is_default;
|
|||
|
+
|
|||
|
+ LIST_INSERT_HEAD(&conf_bindings[conf_hash (section)], node, link);
|
|||
|
+ return 0;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Parse the line LINE of SZ bytes. Skip Comments, recognize section
|
|||
|
+ * headers and feed tag-value pairs into our configuration database.
|
|||
|
+ */
|
|||
|
+static void
|
|||
|
+conf_parse_line(int trans, char *line, size_t sz)
|
|||
|
+{
|
|||
|
+ char *val, *ptr;
|
|||
|
+ size_t i;
|
|||
|
+ int j;
|
|||
|
+ static char *section = 0;
|
|||
|
+ static char *arg = 0;
|
|||
|
+ static int ln = 0;
|
|||
|
+
|
|||
|
+ /* Lines starting with '#' or ';' are comments. */
|
|||
|
+ ln++;
|
|||
|
+ /* Ignore blank lines */
|
|||
|
+ if (*line == '\0')
|
|||
|
+ return;
|
|||
|
+
|
|||
|
+ /* Strip off any leading blanks */
|
|||
|
+ while (isblank(*line))
|
|||
|
+ line++;
|
|||
|
+
|
|||
|
+ if (*line == '#' || *line == ';')
|
|||
|
+ return;
|
|||
|
+
|
|||
|
+ /* '[section]' parsing... */
|
|||
|
+ if (*line == '[') {
|
|||
|
+ line++;
|
|||
|
+ /* Strip off any blanks after '[' */
|
|||
|
+ while (isblank(*line))
|
|||
|
+ line++;
|
|||
|
+ for (i = 0; i < sz; i++) {
|
|||
|
+ if (line[i] == ']') {
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ if (section)
|
|||
|
+ free(section);
|
|||
|
+ if (i == sz) {
|
|||
|
+ xlog_warn("config file error: line %d: "
|
|||
|
+ "non-matched ']', ignoring until next section", ln);
|
|||
|
+ section = 0;
|
|||
|
+ return;
|
|||
|
+ }
|
|||
|
+ /* Strip off any blanks before ']' */
|
|||
|
+ val = line;
|
|||
|
+ while (*val && !isblank(*val))
|
|||
|
+ val++, j++;
|
|||
|
+ if (*val)
|
|||
|
+ i = j;
|
|||
|
+ section = malloc(i);
|
|||
|
+ if (!section) {
|
|||
|
+ xlog_warn("conf_parse_line: %d: malloc (%lu) failed", ln,
|
|||
|
+ (unsigned long)i);
|
|||
|
+ return;
|
|||
|
+ }
|
|||
|
+ strncpy(section, line, i);
|
|||
|
+
|
|||
|
+ if (arg)
|
|||
|
+ free(arg);
|
|||
|
+ arg = 0;
|
|||
|
+
|
|||
|
+ ptr = strchr(val, '"');
|
|||
|
+ if (ptr == NULL)
|
|||
|
+ return;
|
|||
|
+ line = ++ptr;
|
|||
|
+ while (*ptr && *ptr != '"')
|
|||
|
+ ptr++;
|
|||
|
+ if (*ptr == '\0') {
|
|||
|
+ xlog_warn("config file error: line %d: "
|
|||
|
+ "non-matched '\"', ignoring until next section", ln);
|
|||
|
+ } else {
|
|||
|
+ *ptr = '\0';
|
|||
|
+ arg = strdup(line);
|
|||
|
+ if (!arg)
|
|||
|
+ xlog_warn("conf_parse_line: %d: malloc arg failed", ln);
|
|||
|
+ }
|
|||
|
+ return;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ /* Deal with assignments. */
|
|||
|
+ for (i = 0; i < sz; i++) {
|
|||
|
+ if (line[i] == '=') {
|
|||
|
+ /* If no section, we are ignoring the lines. */
|
|||
|
+ if (!section) {
|
|||
|
+ xlog_warn("config file error: line %d: "
|
|||
|
+ "ignoring line due to no section", ln);
|
|||
|
+ return;
|
|||
|
+ }
|
|||
|
+ line[strcspn (line, " \t=")] = '\0';
|
|||
|
+ val = line + i + 1 + strspn (line + i + 1, " \t");
|
|||
|
+
|
|||
|
+ /* Skip trailing comments, if any */
|
|||
|
+ for (j = 0; j < sz - (val - line); j++) {
|
|||
|
+ if (val[j] == '#' || val[j] == ';') {
|
|||
|
+ val[j] = '\0';
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ /* Skip trailing whitespace, if any */
|
|||
|
+ for (j--; j > 0; j--) {
|
|||
|
+ if (isspace(val[j]))
|
|||
|
+ val[j] = '\0';
|
|||
|
+ else
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ /* XXX Perhaps should we not ignore errors? */
|
|||
|
+ conf_set(trans, section, arg, line, val, 0, 0);
|
|||
|
+ return;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ /* Other non-empty lines are weird. */
|
|||
|
+ i = strspn(line, " \t");
|
|||
|
+ if (line[i])
|
|||
|
+ xlog_warn("config file error: line %d:", ln);
|
|||
|
+
|
|||
|
+ return;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/* Parse the mapped configuration file. */
|
|||
|
+static void
|
|||
|
+conf_parse(int trans, char *buf, size_t sz)
|
|||
|
+{
|
|||
|
+ char *cp = buf;
|
|||
|
+ char *bufend = buf + sz;
|
|||
|
+ char *line;
|
|||
|
+
|
|||
|
+ line = cp;
|
|||
|
+ while (cp < bufend) {
|
|||
|
+ if (*cp == '\n') {
|
|||
|
+ /* Check for escaped newlines. */
|
|||
|
+ if (cp > buf && *(cp - 1) == '\\')
|
|||
|
+ *(cp - 1) = *cp = ' ';
|
|||
|
+ else {
|
|||
|
+ *cp = '\0';
|
|||
|
+ conf_parse_line(trans, line, cp - line);
|
|||
|
+ line = cp + 1;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ cp++;
|
|||
|
+ }
|
|||
|
+ if (cp != line)
|
|||
|
+ xlog_warn("conf_parse: last line non-terminated, ignored.");
|
|||
|
+}
|
|||
|
+
|
|||
|
+static void
|
|||
|
+conf_load_defaults(int tr)
|
|||
|
+{
|
|||
|
+ /* No defaults */
|
|||
|
+ return;
|
|||
|
+}
|
|||
|
+
|
|||
|
+void
|
|||
|
+conf_init (void)
|
|||
|
+{
|
|||
|
+ unsigned int i;
|
|||
|
+
|
|||
|
+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
|
|||
|
+ LIST_INIT (&conf_bindings[i]);
|
|||
|
+
|
|||
|
+ TAILQ_INIT (&conf_trans_queue);
|
|||
|
+ conf_reinit();
|
|||
|
+}
|
|||
|
+
|
|||
|
+/* Open the config file and map it into our address space, then parse it. */
|
|||
|
+void
|
|||
|
+conf_reinit(void)
|
|||
|
+{
|
|||
|
+ struct conf_binding *cb = 0;
|
|||
|
+ int fd, trans;
|
|||
|
+ unsigned int i;
|
|||
|
+ size_t sz;
|
|||
|
+ char *new_conf_addr = 0;
|
|||
|
+ struct stat sb;
|
|||
|
+
|
|||
|
+ if ((stat (conf_path, &sb) == 0) || (errno != ENOENT)) {
|
|||
|
+ sz = sb.st_size;
|
|||
|
+ fd = open (conf_path, O_RDONLY, 0);
|
|||
|
+ if (fd == -1) {
|
|||
|
+ xlog_warn("conf_reinit: open (\"%s\", O_RDONLY) failed", conf_path);
|
|||
|
+ return;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ new_conf_addr = malloc(sz);
|
|||
|
+ if (!new_conf_addr) {
|
|||
|
+ xlog_warn("conf_reinit: malloc (%lu) failed", (unsigned long)sz);
|
|||
|
+ goto fail;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ /* XXX I assume short reads won't happen here. */
|
|||
|
+ if (read (fd, new_conf_addr, sz) != (int)sz) {
|
|||
|
+ xlog_warn("conf_reinit: read (%d, %p, %lu) failed",
|
|||
|
+ fd, new_conf_addr, (unsigned long)sz);
|
|||
|
+ goto fail;
|
|||
|
+ }
|
|||
|
+ close(fd);
|
|||
|
+
|
|||
|
+ trans = conf_begin();
|
|||
|
+ /* XXX Should we not care about errors and rollback? */
|
|||
|
+ conf_parse(trans, new_conf_addr, sz);
|
|||
|
+ }
|
|||
|
+ else
|
|||
|
+ trans = conf_begin();
|
|||
|
+
|
|||
|
+ /* Load default configuration values. */
|
|||
|
+ conf_load_defaults(trans);
|
|||
|
+
|
|||
|
+ /* Free potential existing configuration. */
|
|||
|
+ if (conf_addr) {
|
|||
|
+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) {
|
|||
|
+ cb = LIST_FIRST (&conf_bindings[i]);
|
|||
|
+ for (; cb; cb = LIST_FIRST (&conf_bindings[i]))
|
|||
|
+ conf_remove_now(cb->section, cb->tag);
|
|||
|
+ }
|
|||
|
+ free (conf_addr);
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ conf_end(trans, 1);
|
|||
|
+ conf_addr = new_conf_addr;
|
|||
|
+ return;
|
|||
|
+
|
|||
|
+fail:
|
|||
|
+ if (new_conf_addr)
|
|||
|
+ free(new_conf_addr);
|
|||
|
+ close (fd);
|
|||
|
+}
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Return the numeric value denoted by TAG in section SECTION or DEF
|
|||
|
+ * if that tag does not exist.
|
|||
|
+ */
|
|||
|
+int
|
|||
|
+conf_get_num(char *section, char *tag, int def)
|
|||
|
+{
|
|||
|
+ char *value = conf_get_str(section, tag);
|
|||
|
+
|
|||
|
+ if (value)
|
|||
|
+ return atoi(value);
|
|||
|
+
|
|||
|
+ return def;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/* Validate X according to the range denoted by TAG in section SECTION. */
|
|||
|
+int
|
|||
|
+conf_match_num(char *section, char *tag, int x)
|
|||
|
+{
|
|||
|
+ char *value = conf_get_str (section, tag);
|
|||
|
+ int val, min, max, n;
|
|||
|
+
|
|||
|
+ if (!value)
|
|||
|
+ return 0;
|
|||
|
+ n = sscanf (value, "%d,%d:%d", &val, &min, &max);
|
|||
|
+ switch (n) {
|
|||
|
+ case 1:
|
|||
|
+ xlog(LOG_INFO, "conf_match_num: %s:%s %d==%d?", section, tag, val, x);
|
|||
|
+ return x == val;
|
|||
|
+ case 3:
|
|||
|
+ xlog(LOG_INFO, "conf_match_num: %s:%s %d<=%d<=%d?", section,
|
|||
|
+ tag, min, x, max);
|
|||
|
+ return min <= x && max >= x;
|
|||
|
+ default:
|
|||
|
+ xlog(LOG_INFO, "conf_match_num: section %s tag %s: invalid number spec %s",
|
|||
|
+ section, tag, value);
|
|||
|
+ }
|
|||
|
+ return 0;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/* Return the string value denoted by TAG in section SECTION. */
|
|||
|
+char *
|
|||
|
+conf_get_str(char *section, char *tag)
|
|||
|
+{
|
|||
|
+ struct conf_binding *cb;
|
|||
|
+
|
|||
|
+ cb = LIST_FIRST (&conf_bindings[conf_hash (section)]);
|
|||
|
+ for (; cb; cb = LIST_NEXT (cb, link)) {
|
|||
|
+ if (strcasecmp (section, cb->section) == 0
|
|||
|
+ && strcasecmp (tag, cb->tag) == 0)
|
|||
|
+ return cb->value;
|
|||
|
+ }
|
|||
|
+ return 0;
|
|||
|
+}
|
|||
|
+/*
|
|||
|
+ * Find a section that may or may not have an argument
|
|||
|
+ */
|
|||
|
+char *
|
|||
|
+conf_get_section(char *section, char *arg, char *tag)
|
|||
|
+{
|
|||
|
+ struct conf_binding *cb;
|
|||
|
+
|
|||
|
+ cb = LIST_FIRST (&conf_bindings[conf_hash (section)]);
|
|||
|
+ for (; cb; cb = LIST_NEXT (cb, link)) {
|
|||
|
+ if (strcasecmp(section, cb->section) != 0)
|
|||
|
+ continue;
|
|||
|
+ if (arg && strcasecmp(arg, cb->arg) != 0)
|
|||
|
+ continue;
|
|||
|
+ if (strcasecmp(tag, cb->tag) != 0)
|
|||
|
+ continue;
|
|||
|
+ return cb->value;
|
|||
|
+ }
|
|||
|
+ return 0;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Build a list of string values out of the comma separated value denoted by
|
|||
|
+ * TAG in SECTION.
|
|||
|
+ */
|
|||
|
+struct conf_list *
|
|||
|
+conf_get_list(char *section, char *tag)
|
|||
|
+{
|
|||
|
+ char *liststr = 0, *p, *field, *t;
|
|||
|
+ struct conf_list *list = 0;
|
|||
|
+ struct conf_list_node *node;
|
|||
|
+
|
|||
|
+ list = malloc (sizeof *list);
|
|||
|
+ if (!list)
|
|||
|
+ goto cleanup;
|
|||
|
+ TAILQ_INIT (&list->fields);
|
|||
|
+ list->cnt = 0;
|
|||
|
+ liststr = conf_get_str(section, tag);
|
|||
|
+ if (!liststr)
|
|||
|
+ goto cleanup;
|
|||
|
+ liststr = strdup (liststr);
|
|||
|
+ if (!liststr)
|
|||
|
+ goto cleanup;
|
|||
|
+ p = liststr;
|
|||
|
+ while ((field = strsep (&p, ",")) != NULL) {
|
|||
|
+ /* Skip leading whitespace */
|
|||
|
+ while (isspace (*field))
|
|||
|
+ field++;
|
|||
|
+ /* Skip trailing whitespace */
|
|||
|
+ if (p) {
|
|||
|
+ for (t = p - 1; t > field && isspace (*t); t--)
|
|||
|
+ *t = '\0';
|
|||
|
+ }
|
|||
|
+ if (*field == '\0') {
|
|||
|
+ xlog(LOG_INFO, "conf_get_list: empty field, ignoring...");
|
|||
|
+ continue;
|
|||
|
+ }
|
|||
|
+ list->cnt++;
|
|||
|
+ node = calloc (1, sizeof *node);
|
|||
|
+ if (!node)
|
|||
|
+ goto cleanup;
|
|||
|
+ node->field = strdup (field);
|
|||
|
+ if (!node->field) {
|
|||
|
+ free(node);
|
|||
|
+ goto cleanup;
|
|||
|
+ }
|
|||
|
+ TAILQ_INSERT_TAIL (&list->fields, node, link);
|
|||
|
+ }
|
|||
|
+ free (liststr);
|
|||
|
+ return list;
|
|||
|
+
|
|||
|
+cleanup:
|
|||
|
+ if (list)
|
|||
|
+ conf_free_list(list);
|
|||
|
+ if (liststr)
|
|||
|
+ free(liststr);
|
|||
|
+ return 0;
|
|||
|
+}
|
|||
|
+
|
|||
|
+struct conf_list *
|
|||
|
+conf_get_tag_list(char *section)
|
|||
|
+{
|
|||
|
+ struct conf_list *list = 0;
|
|||
|
+ struct conf_list_node *node;
|
|||
|
+ struct conf_binding *cb;
|
|||
|
+
|
|||
|
+ list = malloc(sizeof *list);
|
|||
|
+ if (!list)
|
|||
|
+ goto cleanup;
|
|||
|
+ TAILQ_INIT(&list->fields);
|
|||
|
+ list->cnt = 0;
|
|||
|
+ cb = LIST_FIRST(&conf_bindings[conf_hash (section)]);
|
|||
|
+ for (; cb; cb = LIST_NEXT(cb, link)) {
|
|||
|
+ if (strcasecmp (section, cb->section) == 0) {
|
|||
|
+ list->cnt++;
|
|||
|
+ node = calloc(1, sizeof *node);
|
|||
|
+ if (!node)
|
|||
|
+ goto cleanup;
|
|||
|
+ node->field = strdup(cb->tag);
|
|||
|
+ if (!node->field) {
|
|||
|
+ free(node);
|
|||
|
+ goto cleanup;
|
|||
|
+ }
|
|||
|
+ TAILQ_INSERT_TAIL(&list->fields, node, link);
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ return list;
|
|||
|
+
|
|||
|
+cleanup:
|
|||
|
+ if (list)
|
|||
|
+ conf_free_list(list);
|
|||
|
+ return 0;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/* Decode a PEM encoded buffer. */
|
|||
|
+int
|
|||
|
+conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf)
|
|||
|
+{
|
|||
|
+ u_int32_t c = 0;
|
|||
|
+ u_int8_t c1, c2, c3, c4;
|
|||
|
+
|
|||
|
+ while (*buf) {
|
|||
|
+ if (*buf > 127 || (c1 = asc2bin[*buf]) == 255)
|
|||
|
+ return 0;
|
|||
|
+
|
|||
|
+ buf++;
|
|||
|
+ if (*buf > 127 || (c2 = asc2bin[*buf]) == 255)
|
|||
|
+ return 0;
|
|||
|
+
|
|||
|
+ buf++;
|
|||
|
+ if (*buf == '=') {
|
|||
|
+ c3 = c4 = 0;
|
|||
|
+ c++;
|
|||
|
+
|
|||
|
+ /* Check last four bit */
|
|||
|
+ if (c2 & 0xF)
|
|||
|
+ return 0;
|
|||
|
+
|
|||
|
+ if (strcmp((char *)buf, "==") == 0)
|
|||
|
+ buf++;
|
|||
|
+ else
|
|||
|
+ return 0;
|
|||
|
+ } else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255)
|
|||
|
+ return 0;
|
|||
|
+ else {
|
|||
|
+ if (*++buf == '=') {
|
|||
|
+ c4 = 0;
|
|||
|
+ c += 2;
|
|||
|
+
|
|||
|
+ /* Check last two bit */
|
|||
|
+ if (c3 & 3)
|
|||
|
+ return 0;
|
|||
|
+
|
|||
|
+ if (strcmp((char *)buf, "="))
|
|||
|
+ return 0;
|
|||
|
+ } else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255)
|
|||
|
+ return 0;
|
|||
|
+ else
|
|||
|
+ c += 3;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ buf++;
|
|||
|
+ *out++ = (c1 << 2) | (c2 >> 4);
|
|||
|
+ *out++ = (c2 << 4) | (c3 >> 2);
|
|||
|
+ *out++ = (c3 << 6) | c4;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ *len = c;
|
|||
|
+ return 1;
|
|||
|
+}
|
|||
|
+
|
|||
|
+void
|
|||
|
+conf_free_list(struct conf_list *list)
|
|||
|
+{
|
|||
|
+ struct conf_list_node *node = TAILQ_FIRST(&list->fields);
|
|||
|
+
|
|||
|
+ while (node) {
|
|||
|
+ TAILQ_REMOVE(&list->fields, node, link);
|
|||
|
+ if (node->field)
|
|||
|
+ free(node->field);
|
|||
|
+ free (node);
|
|||
|
+ node = TAILQ_FIRST(&list->fields);
|
|||
|
+ }
|
|||
|
+ free (list);
|
|||
|
+}
|
|||
|
+
|
|||
|
+int
|
|||
|
+conf_begin(void)
|
|||
|
+{
|
|||
|
+ static int seq = 0;
|
|||
|
+
|
|||
|
+ return ++seq;
|
|||
|
+}
|
|||
|
+
|
|||
|
+static struct conf_trans *
|
|||
|
+conf_trans_node(int transaction, enum conf_op op)
|
|||
|
+{
|
|||
|
+ struct conf_trans *node;
|
|||
|
+
|
|||
|
+ node = calloc (1, sizeof *node);
|
|||
|
+ if (!node) {
|
|||
|
+ xlog_warn("conf_trans_node: calloc (1, %lu) failed",
|
|||
|
+ (unsigned long)sizeof *node);
|
|||
|
+ return 0;
|
|||
|
+ }
|
|||
|
+ node->trans = transaction;
|
|||
|
+ node->op = op;
|
|||
|
+ TAILQ_INSERT_TAIL (&conf_trans_queue, node, link);
|
|||
|
+ return node;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/* Queue a set operation. */
|
|||
|
+static int
|
|||
|
+conf_set(int transaction, char *section, char *arg,
|
|||
|
+ char *tag, char *value, int override, int is_default)
|
|||
|
+{
|
|||
|
+ struct conf_trans *node;
|
|||
|
+
|
|||
|
+ node = conf_trans_node(transaction, CONF_SET);
|
|||
|
+ if (!node)
|
|||
|
+ return 1;
|
|||
|
+ node->section = strdup(section);
|
|||
|
+ if (!node->section) {
|
|||
|
+ xlog_warn("conf_set: strdup(\"%s\") failed", section);
|
|||
|
+ goto fail;
|
|||
|
+ }
|
|||
|
+ /* Make Section names case-insensitive */
|
|||
|
+ upper2lower(node->section);
|
|||
|
+
|
|||
|
+ if (arg) {
|
|||
|
+ node->arg = strdup(arg);
|
|||
|
+ if (!node->arg) {
|
|||
|
+ xlog_warn("conf_set: strdup(\"%s\") failed", arg);
|
|||
|
+ goto fail;
|
|||
|
+ }
|
|||
|
+ } else
|
|||
|
+ node->arg = NULL;
|
|||
|
+
|
|||
|
+ node->tag = strdup(tag);
|
|||
|
+ if (!node->tag) {
|
|||
|
+ xlog_warn("conf_set: strdup(\"%s\") failed", tag);
|
|||
|
+ goto fail;
|
|||
|
+ }
|
|||
|
+ node->value = strdup(value);
|
|||
|
+ if (!node->value) {
|
|||
|
+ xlog_warn("conf_set: strdup(\"%s\") failed", value);
|
|||
|
+ goto fail;
|
|||
|
+ }
|
|||
|
+ node->override = override;
|
|||
|
+ node->is_default = is_default;
|
|||
|
+ return 0;
|
|||
|
+
|
|||
|
+fail:
|
|||
|
+ if (node->tag)
|
|||
|
+ free(node->tag);
|
|||
|
+ if (node->section)
|
|||
|
+ free(node->section);
|
|||
|
+ if (node)
|
|||
|
+ free(node);
|
|||
|
+ return 1;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/* Queue a remove operation. */
|
|||
|
+int
|
|||
|
+conf_remove(int transaction, char *section, char *tag)
|
|||
|
+{
|
|||
|
+ struct conf_trans *node;
|
|||
|
+
|
|||
|
+ node = conf_trans_node(transaction, CONF_REMOVE);
|
|||
|
+ if (!node)
|
|||
|
+ goto fail;
|
|||
|
+ node->section = strdup(section);
|
|||
|
+ if (!node->section) {
|
|||
|
+ xlog_warn("conf_remove: strdup(\"%s\") failed", section);
|
|||
|
+ goto fail;
|
|||
|
+ }
|
|||
|
+ node->tag = strdup(tag);
|
|||
|
+ if (!node->tag) {
|
|||
|
+ xlog_warn("conf_remove: strdup(\"%s\") failed", tag);
|
|||
|
+ goto fail;
|
|||
|
+ }
|
|||
|
+ return 0;
|
|||
|
+
|
|||
|
+fail:
|
|||
|
+ if (node && node->section)
|
|||
|
+ free (node->section);
|
|||
|
+ if (node)
|
|||
|
+ free (node);
|
|||
|
+ return 1;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/* Queue a remove section operation. */
|
|||
|
+int
|
|||
|
+conf_remove_section(int transaction, char *section)
|
|||
|
+{
|
|||
|
+ struct conf_trans *node;
|
|||
|
+
|
|||
|
+ node = conf_trans_node(transaction, CONF_REMOVE_SECTION);
|
|||
|
+ if (!node)
|
|||
|
+ goto fail;
|
|||
|
+ node->section = strdup(section);
|
|||
|
+ if (!node->section) {
|
|||
|
+ xlog_warn("conf_remove_section: strdup(\"%s\") failed", section);
|
|||
|
+ goto fail;
|
|||
|
+ }
|
|||
|
+ return 0;
|
|||
|
+
|
|||
|
+fail:
|
|||
|
+ if (node)
|
|||
|
+ free(node);
|
|||
|
+ return 1;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/* Execute all queued operations for this transaction. Cleanup. */
|
|||
|
+int
|
|||
|
+conf_end(int transaction, int commit)
|
|||
|
+{
|
|||
|
+ struct conf_trans *node, *next;
|
|||
|
+
|
|||
|
+ for (node = TAILQ_FIRST(&conf_trans_queue); node; node = next) {
|
|||
|
+ next = TAILQ_NEXT(node, link);
|
|||
|
+ if (node->trans == transaction) {
|
|||
|
+ if (commit) {
|
|||
|
+ switch (node->op) {
|
|||
|
+ case CONF_SET:
|
|||
|
+ conf_set_now(node->section, node->arg,
|
|||
|
+ node->tag, node->value, node->override,
|
|||
|
+ node->is_default);
|
|||
|
+ break;
|
|||
|
+ case CONF_REMOVE:
|
|||
|
+ conf_remove_now(node->section, node->tag);
|
|||
|
+ break;
|
|||
|
+ case CONF_REMOVE_SECTION:
|
|||
|
+ conf_remove_section_now(node->section);
|
|||
|
+ break;
|
|||
|
+ default:
|
|||
|
+ xlog(LOG_INFO, "conf_end: unknown operation: %d", node->op);
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ TAILQ_REMOVE (&conf_trans_queue, node, link);
|
|||
|
+ if (node->section)
|
|||
|
+ free(node->section);
|
|||
|
+ if (node->tag)
|
|||
|
+ free(node->tag);
|
|||
|
+ if (node->value)
|
|||
|
+ free(node->value);
|
|||
|
+ free (node);
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ return 0;
|
|||
|
+}
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Dump running configuration upon SIGUSR1.
|
|||
|
+ * Configuration is "stored in reverse order", so reverse it again.
|
|||
|
+ */
|
|||
|
+struct dumper {
|
|||
|
+ char *s, *v;
|
|||
|
+ struct dumper *next;
|
|||
|
+};
|
|||
|
+
|
|||
|
+static void
|
|||
|
+conf_report_dump(struct dumper *node)
|
|||
|
+{
|
|||
|
+ /* Recursive, cleanup when we're done. */
|
|||
|
+ if (node->next)
|
|||
|
+ conf_report_dump(node->next);
|
|||
|
+
|
|||
|
+ if (node->v)
|
|||
|
+ xlog(LOG_INFO, "%s=\t%s", node->s, node->v);
|
|||
|
+ else if (node->s) {
|
|||
|
+ xlog(LOG_INFO, "%s", node->s);
|
|||
|
+ if (strlen(node->s) > 0)
|
|||
|
+ free(node->s);
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ free (node);
|
|||
|
+}
|
|||
|
+
|
|||
|
+void
|
|||
|
+conf_report (void)
|
|||
|
+{
|
|||
|
+ struct conf_binding *cb, *last = 0;
|
|||
|
+ unsigned int i, len, diff_arg = 0;
|
|||
|
+ char *current_section = (char *)0;
|
|||
|
+ char *current_arg = (char *)0;
|
|||
|
+ struct dumper *dumper, *dnode;
|
|||
|
+
|
|||
|
+ dumper = dnode = (struct dumper *)calloc(1, sizeof *dumper);
|
|||
|
+ if (!dumper)
|
|||
|
+ goto mem_fail;
|
|||
|
+
|
|||
|
+ xlog(LOG_INFO, "conf_report: dumping running configuration");
|
|||
|
+
|
|||
|
+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
|
|||
|
+ for (cb = LIST_FIRST(&conf_bindings[i]); cb; cb = LIST_NEXT(cb, link)) {
|
|||
|
+ if (!cb->is_default) {
|
|||
|
+ /* Make sure the Section arugment is the same */
|
|||
|
+ if (current_arg && current_section && cb->arg) {
|
|||
|
+ if (strcmp(cb->section, current_section) == 0 &&
|
|||
|
+ strcmp(cb->arg, current_arg) != 0)
|
|||
|
+ diff_arg = 1;
|
|||
|
+ }
|
|||
|
+ /* Dump this entry. */
|
|||
|
+ if (!current_section || strcmp(cb->section, current_section)
|
|||
|
+ || diff_arg) {
|
|||
|
+ if (current_section || diff_arg) {
|
|||
|
+ len = strlen (current_section) + 3;
|
|||
|
+ if (current_arg)
|
|||
|
+ len += strlen(current_arg) + 3;
|
|||
|
+ dnode->s = malloc(len);
|
|||
|
+ if (!dnode->s)
|
|||
|
+ goto mem_fail;
|
|||
|
+
|
|||
|
+ if (current_arg)
|
|||
|
+ snprintf(dnode->s, len, "[%s \"%s\"]",
|
|||
|
+ current_section, current_arg);
|
|||
|
+ else
|
|||
|
+ snprintf(dnode->s, len, "[%s]", current_section);
|
|||
|
+
|
|||
|
+ dnode->next =
|
|||
|
+ (struct dumper *)calloc(1, sizeof (struct dumper));
|
|||
|
+ dnode = dnode->next;
|
|||
|
+ if (!dnode)
|
|||
|
+ goto mem_fail;
|
|||
|
+
|
|||
|
+ dnode->s = "";
|
|||
|
+ dnode->next =
|
|||
|
+ (struct dumper *)calloc(1, sizeof (struct dumper));
|
|||
|
+ dnode = dnode->next;
|
|||
|
+ if (!dnode)
|
|||
|
+ goto mem_fail;
|
|||
|
+ }
|
|||
|
+ current_section = cb->section;
|
|||
|
+ current_arg = cb->arg;
|
|||
|
+ diff_arg = 0;
|
|||
|
+ }
|
|||
|
+ dnode->s = cb->tag;
|
|||
|
+ dnode->v = cb->value;
|
|||
|
+ dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper));
|
|||
|
+ dnode = dnode->next;
|
|||
|
+ if (!dnode)
|
|||
|
+ goto mem_fail;
|
|||
|
+ last = cb;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ if (last) {
|
|||
|
+ len = strlen(last->section) + 3;
|
|||
|
+ if (last->arg)
|
|||
|
+ len += strlen(last->arg) + 3;
|
|||
|
+ dnode->s = malloc(len);
|
|||
|
+ if (!dnode->s)
|
|||
|
+ goto mem_fail;
|
|||
|
+ if (last->arg)
|
|||
|
+ snprintf(dnode->s, len, "[%s \"%s\"]", last->section, last->arg);
|
|||
|
+ else
|
|||
|
+ snprintf(dnode->s, len, "[%s]", last->section);
|
|||
|
+ }
|
|||
|
+ conf_report_dump(dumper);
|
|||
|
+ return;
|
|||
|
+
|
|||
|
+mem_fail:
|
|||
|
+ xlog_warn("conf_report: malloc/calloc failed");
|
|||
|
+ while ((dnode = dumper) != 0) {
|
|||
|
+ dumper = dumper->next;
|
|||
|
+ if (dnode->s)
|
|||
|
+ free(dnode->s);
|
|||
|
+ free(dnode);
|
|||
|
+ }
|
|||
|
+ return;
|
|||
|
+}
|
|||
|
diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am
|
|||
|
index eb393df..4dabb3d 100644
|
|||
|
--- a/utils/idmapd/Makefile.am
|
|||
|
+++ b/utils/idmapd/Makefile.am
|
|||
|
@@ -14,7 +14,6 @@ EXTRA_DIST = \
|
|||
|
|
|||
|
idmapd_SOURCES = \
|
|||
|
atomicio.c \
|
|||
|
- cfg.c \
|
|||
|
idmapd.c \
|
|||
|
strlcat.c \
|
|||
|
strlcpy.c \
|
|||
|
diff --git a/utils/idmapd/cfg.c b/utils/idmapd/cfg.c
|
|||
|
deleted file mode 100644
|
|||
|
index 16d392a..0000000
|
|||
|
--- a/utils/idmapd/cfg.c
|
|||
|
+++ /dev/null
|
|||
|
@@ -1,893 +0,0 @@
|
|||
|
-/* $OpenBSD: conf.c,v 1.55 2003/06/03 14:28:16 ho Exp $ */
|
|||
|
-/* $EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $ */
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
|
|||
|
- * Copyright (c) 2000, 2001, 2002 H<>kan Olsson. All rights reserved.
|
|||
|
- *
|
|||
|
- * Redistribution and use in source and binary forms, with or without
|
|||
|
- * modification, are permitted provided that the following conditions
|
|||
|
- * are met:
|
|||
|
- * 1. Redistributions of source code must retain the above copyright
|
|||
|
- * notice, this list of conditions and the following disclaimer.
|
|||
|
- * 2. Redistributions in binary form must reproduce the above copyright
|
|||
|
- * notice, this list of conditions and the following disclaimer in the
|
|||
|
- * documentation and/or other materials provided with the distribution.
|
|||
|
- *
|
|||
|
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|||
|
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|||
|
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|||
|
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|||
|
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|||
|
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
|
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
|
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
|
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|||
|
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
- */
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * This code was written under funding by Ericsson Radio Systems.
|
|||
|
- */
|
|||
|
-
|
|||
|
-#include <sys/param.h>
|
|||
|
-#include <sys/mman.h>
|
|||
|
-#include <sys/socket.h>
|
|||
|
-#include <sys/stat.h>
|
|||
|
-#include <netinet/in.h>
|
|||
|
-#include <arpa/inet.h>
|
|||
|
-#include <ctype.h>
|
|||
|
-#include <fcntl.h>
|
|||
|
-#include <stdio.h>
|
|||
|
-#include <stdlib.h>
|
|||
|
-#include <string.h>
|
|||
|
-#include <unistd.h>
|
|||
|
-#include <errno.h>
|
|||
|
-#include <err.h>
|
|||
|
-
|
|||
|
-#include "cfg.h"
|
|||
|
-
|
|||
|
-static void conf_load_defaults (int);
|
|||
|
-#if 0
|
|||
|
-static int conf_find_trans_xf (int, char *);
|
|||
|
-#endif
|
|||
|
-
|
|||
|
-size_t strlcpy(char *, const char *, size_t);
|
|||
|
-
|
|||
|
-struct conf_trans {
|
|||
|
- TAILQ_ENTRY (conf_trans) link;
|
|||
|
- int trans;
|
|||
|
- enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op;
|
|||
|
- char *section;
|
|||
|
- char *tag;
|
|||
|
- char *value;
|
|||
|
- int override;
|
|||
|
- int is_default;
|
|||
|
-};
|
|||
|
-
|
|||
|
-TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue;
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * Radix-64 Encoding.
|
|||
|
- */
|
|||
|
-const u_int8_t bin2asc[]
|
|||
|
- = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|||
|
-
|
|||
|
-const u_int8_t asc2bin[] =
|
|||
|
-{
|
|||
|
- 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
- 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
- 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
- 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
- 255, 255, 255, 255, 255, 255, 255, 255,
|
|||
|
- 255, 255, 255, 62, 255, 255, 255, 63,
|
|||
|
- 52, 53, 54, 55, 56, 57, 58, 59,
|
|||
|
- 60, 61, 255, 255, 255, 255, 255, 255,
|
|||
|
- 255, 0, 1, 2, 3, 4, 5, 6,
|
|||
|
- 7, 8, 9, 10, 11, 12, 13, 14,
|
|||
|
- 15, 16, 17, 18, 19, 20, 21, 22,
|
|||
|
- 23, 24, 25, 255, 255, 255, 255, 255,
|
|||
|
- 255, 26, 27, 28, 29, 30, 31, 32,
|
|||
|
- 33, 34, 35, 36, 37, 38, 39, 40,
|
|||
|
- 41, 42, 43, 44, 45, 46, 47, 48,
|
|||
|
- 49, 50, 51, 255, 255, 255, 255, 255
|
|||
|
-};
|
|||
|
-
|
|||
|
-struct conf_binding {
|
|||
|
- LIST_ENTRY (conf_binding) link;
|
|||
|
- char *section;
|
|||
|
- char *tag;
|
|||
|
- char *value;
|
|||
|
- int is_default;
|
|||
|
-};
|
|||
|
-
|
|||
|
-char *conf_path;
|
|||
|
-LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256];
|
|||
|
-
|
|||
|
-static char *conf_addr;
|
|||
|
-
|
|||
|
-static __inline__ u_int8_t
|
|||
|
-conf_hash (char *s)
|
|||
|
-{
|
|||
|
- u_int8_t hash = 0;
|
|||
|
-
|
|||
|
- while (*s)
|
|||
|
- {
|
|||
|
- hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s);
|
|||
|
- s++;
|
|||
|
- }
|
|||
|
- return hash;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * Insert a tag-value combination from LINE (the equal sign is at POS)
|
|||
|
- */
|
|||
|
-static int
|
|||
|
-conf_remove_now (char *section, char *tag)
|
|||
|
-{
|
|||
|
- struct conf_binding *cb, *next;
|
|||
|
-
|
|||
|
- for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
|
|||
|
- {
|
|||
|
- next = LIST_NEXT (cb, link);
|
|||
|
- if (strcasecmp (cb->section, section) == 0
|
|||
|
- && strcasecmp (cb->tag, tag) == 0)
|
|||
|
- {
|
|||
|
- LIST_REMOVE (cb, link);
|
|||
|
- warnx("[%s]:%s->%s removed", section, tag, cb->value);
|
|||
|
- free (cb->section);
|
|||
|
- free (cb->tag);
|
|||
|
- free (cb->value);
|
|||
|
- free (cb);
|
|||
|
- return 0;
|
|||
|
- }
|
|||
|
- }
|
|||
|
- return 1;
|
|||
|
-}
|
|||
|
-
|
|||
|
-static int
|
|||
|
-conf_remove_section_now (char *section)
|
|||
|
-{
|
|||
|
- struct conf_binding *cb, *next;
|
|||
|
- int unseen = 1;
|
|||
|
-
|
|||
|
- for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
|
|||
|
- {
|
|||
|
- next = LIST_NEXT (cb, link);
|
|||
|
- if (strcasecmp (cb->section, section) == 0)
|
|||
|
- {
|
|||
|
- unseen = 0;
|
|||
|
- LIST_REMOVE (cb, link);
|
|||
|
- warnx("[%s]:%s->%s removed", section, cb->tag, cb->value);
|
|||
|
- free (cb->section);
|
|||
|
- free (cb->tag);
|
|||
|
- free (cb->value);
|
|||
|
- free (cb);
|
|||
|
- }
|
|||
|
- }
|
|||
|
- return unseen;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * Insert a tag-value combination from LINE (the equal sign is at POS)
|
|||
|
- * into SECTION of our configuration database.
|
|||
|
- */
|
|||
|
-static int
|
|||
|
-conf_set_now (char *section, char *tag, char *value, int override,
|
|||
|
- int is_default)
|
|||
|
-{
|
|||
|
- struct conf_binding *node = 0;
|
|||
|
-
|
|||
|
- if (override)
|
|||
|
- conf_remove_now (section, tag);
|
|||
|
- else if (conf_get_str (section, tag))
|
|||
|
- {
|
|||
|
- if (!is_default)
|
|||
|
- warnx("conf_set: duplicate tag [%s]:%s, ignoring...\n", section, tag);
|
|||
|
- return 1;
|
|||
|
- }
|
|||
|
-
|
|||
|
- node = calloc (1, sizeof *node);
|
|||
|
- if (!node)
|
|||
|
- {
|
|||
|
- warnx("conf_set: calloc (1, %lu) failed", (unsigned long)sizeof *node);
|
|||
|
- return 1;
|
|||
|
- }
|
|||
|
- node->section = strdup (section);
|
|||
|
- node->tag = strdup (tag);
|
|||
|
- node->value = strdup (value);
|
|||
|
- node->is_default = is_default;
|
|||
|
-
|
|||
|
- LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link);
|
|||
|
- return 0;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * Parse the line LINE of SZ bytes. Skip Comments, recognize section
|
|||
|
- * headers and feed tag-value pairs into our configuration database.
|
|||
|
- */
|
|||
|
-static void
|
|||
|
-conf_parse_line (int trans, char *line, size_t sz)
|
|||
|
-{
|
|||
|
- char *val;
|
|||
|
- size_t i;
|
|||
|
- int j;
|
|||
|
- static char *section = 0;
|
|||
|
- static int ln = 0;
|
|||
|
-
|
|||
|
- ln++;
|
|||
|
-
|
|||
|
- /* Lines starting with '#' or ';' are comments. */
|
|||
|
- if (*line == '#' || *line == ';')
|
|||
|
- return;
|
|||
|
-
|
|||
|
- /* '[section]' parsing... */
|
|||
|
- if (*line == '[')
|
|||
|
- {
|
|||
|
- for (i = 1; i < sz; i++)
|
|||
|
- if (line[i] == ']')
|
|||
|
- break;
|
|||
|
- if (section)
|
|||
|
- free (section);
|
|||
|
- if (i == sz)
|
|||
|
- {
|
|||
|
- warnx("conf_parse_line: %d:"
|
|||
|
- "non-matched ']', ignoring until next section", ln);
|
|||
|
- section = 0;
|
|||
|
- return;
|
|||
|
- }
|
|||
|
- section = malloc (i);
|
|||
|
- if (!section)
|
|||
|
- {
|
|||
|
- warnx("conf_parse_line: %d: malloc (%lu) failed", ln,
|
|||
|
- (unsigned long)i);
|
|||
|
- return;
|
|||
|
- }
|
|||
|
- strlcpy (section, line + 1, i);
|
|||
|
- return;
|
|||
|
- }
|
|||
|
-
|
|||
|
- /* Deal with assignments. */
|
|||
|
- for (i = 0; i < sz; i++)
|
|||
|
- if (line[i] == '=')
|
|||
|
- {
|
|||
|
- /* If no section, we are ignoring the lines. */
|
|||
|
- if (!section)
|
|||
|
- {
|
|||
|
- warnx("conf_parse_line: %d: ignoring line due to no section", ln);
|
|||
|
- return;
|
|||
|
- }
|
|||
|
- line[strcspn (line, " \t=")] = '\0';
|
|||
|
- val = line + i + 1 + strspn (line + i + 1, " \t");
|
|||
|
- /* Skip trailing whitespace, if any */
|
|||
|
- for (j = sz - (val - line) - 1; j > 0 && isspace (val[j]); j--)
|
|||
|
- val[j] = '\0';
|
|||
|
- /* XXX Perhaps should we not ignore errors? */
|
|||
|
- conf_set (trans, section, line, val, 0, 0);
|
|||
|
- return;
|
|||
|
- }
|
|||
|
-
|
|||
|
- /* Other non-empty lines are weird. */
|
|||
|
- i = strspn (line, " \t");
|
|||
|
- if (line[i])
|
|||
|
- warnx("conf_parse_line: %d: syntax error", ln);
|
|||
|
-
|
|||
|
- return;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/* Parse the mapped configuration file. */
|
|||
|
-static void
|
|||
|
-conf_parse (int trans, char *buf, size_t sz)
|
|||
|
-{
|
|||
|
- char *cp = buf;
|
|||
|
- char *bufend = buf + sz;
|
|||
|
- char *line;
|
|||
|
-
|
|||
|
- line = cp;
|
|||
|
- while (cp < bufend)
|
|||
|
- {
|
|||
|
- if (*cp == '\n')
|
|||
|
- {
|
|||
|
- /* Check for escaped newlines. */
|
|||
|
- if (cp > buf && *(cp - 1) == '\\')
|
|||
|
- *(cp - 1) = *cp = ' ';
|
|||
|
- else
|
|||
|
- {
|
|||
|
- *cp = '\0';
|
|||
|
- conf_parse_line (trans, line, cp - line);
|
|||
|
- line = cp + 1;
|
|||
|
- }
|
|||
|
- }
|
|||
|
- cp++;
|
|||
|
- }
|
|||
|
- if (cp != line)
|
|||
|
- warnx("conf_parse: last line non-terminated, ignored.");
|
|||
|
-}
|
|||
|
-
|
|||
|
-static void
|
|||
|
-conf_load_defaults (int tr)
|
|||
|
-{
|
|||
|
- /* No defaults */
|
|||
|
- return;
|
|||
|
-}
|
|||
|
-
|
|||
|
-void
|
|||
|
-conf_init (void)
|
|||
|
-{
|
|||
|
- unsigned int i;
|
|||
|
-
|
|||
|
- for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
|
|||
|
- LIST_INIT (&conf_bindings[i]);
|
|||
|
- TAILQ_INIT (&conf_trans_queue);
|
|||
|
- conf_reinit ();
|
|||
|
-}
|
|||
|
-
|
|||
|
-/* Open the config file and map it into our address space, then parse it. */
|
|||
|
-void
|
|||
|
-conf_reinit (void)
|
|||
|
-{
|
|||
|
- struct conf_binding *cb = 0;
|
|||
|
- int fd, trans;
|
|||
|
- unsigned int i;
|
|||
|
- size_t sz;
|
|||
|
- char *new_conf_addr = 0;
|
|||
|
- struct stat sb;
|
|||
|
-
|
|||
|
- if ((stat (conf_path, &sb) == 0) || (errno != ENOENT))
|
|||
|
- {
|
|||
|
- sz = sb.st_size;
|
|||
|
- fd = open (conf_path, O_RDONLY, 0);
|
|||
|
- if (fd == -1)
|
|||
|
- {
|
|||
|
- warnx("conf_reinit: open (\"%s\", O_RDONLY) failed", conf_path);
|
|||
|
- return;
|
|||
|
- }
|
|||
|
-
|
|||
|
- new_conf_addr = malloc (sz);
|
|||
|
- if (!new_conf_addr)
|
|||
|
- {
|
|||
|
- warnx("conf_reinit: malloc (%lu) failed", (unsigned long)sz);
|
|||
|
- goto fail;
|
|||
|
- }
|
|||
|
-
|
|||
|
- /* XXX I assume short reads won't happen here. */
|
|||
|
- if (read (fd, new_conf_addr, sz) != (int)sz)
|
|||
|
- {
|
|||
|
- warnx("conf_reinit: read (%d, %p, %lu) failed",
|
|||
|
- fd, new_conf_addr, (unsigned long)sz);
|
|||
|
- goto fail;
|
|||
|
- }
|
|||
|
- close (fd);
|
|||
|
-
|
|||
|
- trans = conf_begin ();
|
|||
|
-
|
|||
|
- /* XXX Should we not care about errors and rollback? */
|
|||
|
- conf_parse (trans, new_conf_addr, sz);
|
|||
|
- }
|
|||
|
- else
|
|||
|
- trans = conf_begin ();
|
|||
|
-
|
|||
|
- /* Load default configuration values. */
|
|||
|
- conf_load_defaults (trans);
|
|||
|
-
|
|||
|
- /* Free potential existing configuration. */
|
|||
|
- if (conf_addr)
|
|||
|
- {
|
|||
|
- for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
|
|||
|
- for (cb = LIST_FIRST (&conf_bindings[i]); cb;
|
|||
|
- cb = LIST_FIRST (&conf_bindings[i]))
|
|||
|
- conf_remove_now (cb->section, cb->tag);
|
|||
|
- free (conf_addr);
|
|||
|
- }
|
|||
|
-
|
|||
|
- conf_end (trans, 1);
|
|||
|
- conf_addr = new_conf_addr;
|
|||
|
- return;
|
|||
|
-
|
|||
|
- fail:
|
|||
|
- if (new_conf_addr)
|
|||
|
- free (new_conf_addr);
|
|||
|
- close (fd);
|
|||
|
-}
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * Return the numeric value denoted by TAG in section SECTION or DEF
|
|||
|
- * if that tag does not exist.
|
|||
|
- */
|
|||
|
-int
|
|||
|
-conf_get_num (char *section, char *tag, int def)
|
|||
|
-{
|
|||
|
- char *value = conf_get_str (section, tag);
|
|||
|
-
|
|||
|
- if (value)
|
|||
|
- return atoi (value);
|
|||
|
- return def;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/* Validate X according to the range denoted by TAG in section SECTION. */
|
|||
|
-int
|
|||
|
-conf_match_num (char *section, char *tag, int x)
|
|||
|
-{
|
|||
|
- char *value = conf_get_str (section, tag);
|
|||
|
- int val, min, max, n;
|
|||
|
-
|
|||
|
- if (!value)
|
|||
|
- return 0;
|
|||
|
- n = sscanf (value, "%d,%d:%d", &val, &min, &max);
|
|||
|
- switch (n)
|
|||
|
- {
|
|||
|
- case 1:
|
|||
|
- warnx("conf_match_num: %s:%s %d==%d?", section, tag, val, x);
|
|||
|
- return x == val;
|
|||
|
- case 3:
|
|||
|
- warnx("conf_match_num: %s:%s %d<=%d<=%d?", section, tag, min, x, max);
|
|||
|
- return min <= x && max >= x;
|
|||
|
- default:
|
|||
|
- warnx("conf_match_num: section %s tag %s: invalid number spec %s",
|
|||
|
- section, tag, value);
|
|||
|
- }
|
|||
|
- return 0;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/* Return the string value denoted by TAG in section SECTION. */
|
|||
|
-char *
|
|||
|
-conf_get_str (char *section, char *tag)
|
|||
|
-{
|
|||
|
- struct conf_binding *cb;
|
|||
|
-
|
|||
|
- for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
|
|||
|
- cb = LIST_NEXT (cb, link))
|
|||
|
- if (strcasecmp (section, cb->section) == 0
|
|||
|
- && strcasecmp (tag, cb->tag) == 0)
|
|||
|
- {
|
|||
|
- return cb->value;
|
|||
|
- }
|
|||
|
- return 0;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * Build a list of string values out of the comma separated value denoted by
|
|||
|
- * TAG in SECTION.
|
|||
|
- */
|
|||
|
-struct conf_list *
|
|||
|
-conf_get_list (char *section, char *tag)
|
|||
|
-{
|
|||
|
- char *liststr = 0, *p, *field, *t;
|
|||
|
- struct conf_list *list = 0;
|
|||
|
- struct conf_list_node *node;
|
|||
|
-
|
|||
|
- list = malloc (sizeof *list);
|
|||
|
- if (!list)
|
|||
|
- goto cleanup;
|
|||
|
- TAILQ_INIT (&list->fields);
|
|||
|
- list->cnt = 0;
|
|||
|
- liststr = conf_get_str (section, tag);
|
|||
|
- if (!liststr)
|
|||
|
- goto cleanup;
|
|||
|
- liststr = strdup (liststr);
|
|||
|
- if (!liststr)
|
|||
|
- goto cleanup;
|
|||
|
- p = liststr;
|
|||
|
- while ((field = strsep (&p, ",")) != NULL)
|
|||
|
- {
|
|||
|
- /* Skip leading whitespace */
|
|||
|
- while (isspace (*field))
|
|||
|
- field++;
|
|||
|
- /* Skip trailing whitespace */
|
|||
|
- if (p)
|
|||
|
- for (t = p - 1; t > field && isspace (*t); t--)
|
|||
|
- *t = '\0';
|
|||
|
- if (*field == '\0')
|
|||
|
- {
|
|||
|
- warnx("conf_get_list: empty field, ignoring...");
|
|||
|
- continue;
|
|||
|
- }
|
|||
|
- list->cnt++;
|
|||
|
- node = calloc (1, sizeof *node);
|
|||
|
- if (!node)
|
|||
|
- goto cleanup;
|
|||
|
- node->field = strdup (field);
|
|||
|
- if (!node->field) {
|
|||
|
- free(node);
|
|||
|
- goto cleanup;
|
|||
|
- }
|
|||
|
- TAILQ_INSERT_TAIL (&list->fields, node, link);
|
|||
|
- }
|
|||
|
- free (liststr);
|
|||
|
- return list;
|
|||
|
-
|
|||
|
- cleanup:
|
|||
|
- if (list)
|
|||
|
- conf_free_list (list);
|
|||
|
- if (liststr)
|
|||
|
- free (liststr);
|
|||
|
- return 0;
|
|||
|
-}
|
|||
|
-
|
|||
|
-struct conf_list *
|
|||
|
-conf_get_tag_list (char *section)
|
|||
|
-{
|
|||
|
- struct conf_list *list = 0;
|
|||
|
- struct conf_list_node *node;
|
|||
|
- struct conf_binding *cb;
|
|||
|
-
|
|||
|
- list = malloc (sizeof *list);
|
|||
|
- if (!list)
|
|||
|
- goto cleanup;
|
|||
|
- TAILQ_INIT (&list->fields);
|
|||
|
- list->cnt = 0;
|
|||
|
- for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
|
|||
|
- cb = LIST_NEXT (cb, link))
|
|||
|
- if (strcasecmp (section, cb->section) == 0)
|
|||
|
- {
|
|||
|
- list->cnt++;
|
|||
|
- node = calloc (1, sizeof *node);
|
|||
|
- if (!node)
|
|||
|
- goto cleanup;
|
|||
|
- node->field = strdup (cb->tag);
|
|||
|
- if (!node->field) {
|
|||
|
- free(node);
|
|||
|
- goto cleanup;
|
|||
|
- }
|
|||
|
- TAILQ_INSERT_TAIL (&list->fields, node, link);
|
|||
|
- }
|
|||
|
- return list;
|
|||
|
-
|
|||
|
- cleanup:
|
|||
|
- if (list)
|
|||
|
- conf_free_list (list);
|
|||
|
- return 0;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/* Decode a PEM encoded buffer. */
|
|||
|
-int
|
|||
|
-conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf)
|
|||
|
-{
|
|||
|
- u_int32_t c = 0;
|
|||
|
- u_int8_t c1, c2, c3, c4;
|
|||
|
-
|
|||
|
- while (*buf)
|
|||
|
- {
|
|||
|
- if (*buf > 127 || (c1 = asc2bin[*buf]) == 255)
|
|||
|
- return 0;
|
|||
|
- buf++;
|
|||
|
-
|
|||
|
- if (*buf > 127 || (c2 = asc2bin[*buf]) == 255)
|
|||
|
- return 0;
|
|||
|
- buf++;
|
|||
|
-
|
|||
|
- if (*buf == '=')
|
|||
|
- {
|
|||
|
- c3 = c4 = 0;
|
|||
|
- c++;
|
|||
|
-
|
|||
|
- /* Check last four bit */
|
|||
|
- if (c2 & 0xF)
|
|||
|
- return 0;
|
|||
|
-
|
|||
|
- if (strcmp ((char *)buf, "==") == 0)
|
|||
|
- buf++;
|
|||
|
- else
|
|||
|
- return 0;
|
|||
|
- }
|
|||
|
- else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255)
|
|||
|
- return 0;
|
|||
|
- else
|
|||
|
- {
|
|||
|
- if (*++buf == '=')
|
|||
|
- {
|
|||
|
- c4 = 0;
|
|||
|
- c += 2;
|
|||
|
-
|
|||
|
- /* Check last two bit */
|
|||
|
- if (c3 & 3)
|
|||
|
- return 0;
|
|||
|
-
|
|||
|
- if (strcmp ((char *)buf, "="))
|
|||
|
- return 0;
|
|||
|
-
|
|||
|
- }
|
|||
|
- else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255)
|
|||
|
- return 0;
|
|||
|
- else
|
|||
|
- c += 3;
|
|||
|
- }
|
|||
|
-
|
|||
|
- buf++;
|
|||
|
- *out++ = (c1 << 2) | (c2 >> 4);
|
|||
|
- *out++ = (c2 << 4) | (c3 >> 2);
|
|||
|
- *out++ = (c3 << 6) | c4;
|
|||
|
- }
|
|||
|
-
|
|||
|
- *len = c;
|
|||
|
- return 1;
|
|||
|
-
|
|||
|
-}
|
|||
|
-
|
|||
|
-void
|
|||
|
-conf_free_list (struct conf_list *list)
|
|||
|
-{
|
|||
|
- struct conf_list_node *node = TAILQ_FIRST (&list->fields);
|
|||
|
-
|
|||
|
- while (node)
|
|||
|
- {
|
|||
|
- TAILQ_REMOVE (&list->fields, node, link);
|
|||
|
- if (node->field)
|
|||
|
- free (node->field);
|
|||
|
- free (node);
|
|||
|
- node = TAILQ_FIRST (&list->fields);
|
|||
|
- }
|
|||
|
- free (list);
|
|||
|
-}
|
|||
|
-
|
|||
|
-int
|
|||
|
-conf_begin (void)
|
|||
|
-{
|
|||
|
- static int seq = 0;
|
|||
|
-
|
|||
|
- return ++seq;
|
|||
|
-}
|
|||
|
-
|
|||
|
-static struct conf_trans *
|
|||
|
-conf_trans_node (int transaction, enum conf_op op)
|
|||
|
-{
|
|||
|
- struct conf_trans *node;
|
|||
|
-
|
|||
|
- node = calloc (1, sizeof *node);
|
|||
|
- if (!node)
|
|||
|
- {
|
|||
|
- warnx("conf_trans_node: calloc (1, %lu) failed",
|
|||
|
- (unsigned long)sizeof *node);
|
|||
|
- return 0;
|
|||
|
- }
|
|||
|
- node->trans = transaction;
|
|||
|
- node->op = op;
|
|||
|
- TAILQ_INSERT_TAIL (&conf_trans_queue, node, link);
|
|||
|
- return node;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/* Queue a set operation. */
|
|||
|
-int
|
|||
|
-conf_set (int transaction, char *section, char *tag, char *value, int override,
|
|||
|
- int is_default)
|
|||
|
-{
|
|||
|
- struct conf_trans *node;
|
|||
|
-
|
|||
|
- node = conf_trans_node (transaction, CONF_SET);
|
|||
|
- if (!node)
|
|||
|
- return 1;
|
|||
|
- node->section = strdup (section);
|
|||
|
- if (!node->section)
|
|||
|
- {
|
|||
|
- warnx("conf_set: strdup (\"%s\") failed", section);
|
|||
|
- goto fail;
|
|||
|
- }
|
|||
|
- node->tag = strdup (tag);
|
|||
|
- if (!node->tag)
|
|||
|
- {
|
|||
|
- warnx("conf_set: strdup (\"%s\") failed", tag);
|
|||
|
- goto fail;
|
|||
|
- }
|
|||
|
- node->value = strdup (value);
|
|||
|
- if (!node->value)
|
|||
|
- {
|
|||
|
- warnx("conf_set: strdup (\"%s\") failed", value);
|
|||
|
- goto fail;
|
|||
|
- }
|
|||
|
- node->override = override;
|
|||
|
- node->is_default = is_default;
|
|||
|
- return 0;
|
|||
|
-
|
|||
|
- fail:
|
|||
|
- if (node->tag)
|
|||
|
- free (node->tag);
|
|||
|
- if (node->section)
|
|||
|
- free (node->section);
|
|||
|
- if (node)
|
|||
|
- free (node);
|
|||
|
- return 1;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/* Queue a remove operation. */
|
|||
|
-int
|
|||
|
-conf_remove (int transaction, char *section, char *tag)
|
|||
|
-{
|
|||
|
- struct conf_trans *node;
|
|||
|
-
|
|||
|
- node = conf_trans_node (transaction, CONF_REMOVE);
|
|||
|
- if (!node)
|
|||
|
- goto fail;
|
|||
|
- node->section = strdup (section);
|
|||
|
- if (!node->section)
|
|||
|
- {
|
|||
|
- warnx("conf_remove: strdup (\"%s\") failed", section);
|
|||
|
- goto fail;
|
|||
|
- }
|
|||
|
- node->tag = strdup (tag);
|
|||
|
- if (!node->tag)
|
|||
|
- {
|
|||
|
- warnx("conf_remove: strdup (\"%s\") failed", tag);
|
|||
|
- goto fail;
|
|||
|
- }
|
|||
|
- return 0;
|
|||
|
-
|
|||
|
- fail:
|
|||
|
- if (node && node->section)
|
|||
|
- free (node->section);
|
|||
|
- if (node)
|
|||
|
- free (node);
|
|||
|
- return 1;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/* Queue a remove section operation. */
|
|||
|
-int
|
|||
|
-conf_remove_section (int transaction, char *section)
|
|||
|
-{
|
|||
|
- struct conf_trans *node;
|
|||
|
-
|
|||
|
- node = conf_trans_node (transaction, CONF_REMOVE_SECTION);
|
|||
|
- if (!node)
|
|||
|
- goto fail;
|
|||
|
- node->section = strdup (section);
|
|||
|
- if (!node->section)
|
|||
|
- {
|
|||
|
- warnx("conf_remove_section: strdup (\"%s\") failed", section);
|
|||
|
- goto fail;
|
|||
|
- }
|
|||
|
- return 0;
|
|||
|
-
|
|||
|
- fail:
|
|||
|
- if (node)
|
|||
|
- free (node);
|
|||
|
- return 1;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/* Execute all queued operations for this transaction. Cleanup. */
|
|||
|
-int
|
|||
|
-conf_end (int transaction, int commit)
|
|||
|
-{
|
|||
|
- struct conf_trans *node, *next;
|
|||
|
-
|
|||
|
- for (node = TAILQ_FIRST (&conf_trans_queue); node; node = next)
|
|||
|
- {
|
|||
|
- next = TAILQ_NEXT (node, link);
|
|||
|
- if (node->trans == transaction)
|
|||
|
- {
|
|||
|
- if (commit)
|
|||
|
- switch (node->op)
|
|||
|
- {
|
|||
|
- case CONF_SET:
|
|||
|
- conf_set_now (node->section, node->tag, node->value,
|
|||
|
- node->override, node->is_default);
|
|||
|
- break;
|
|||
|
- case CONF_REMOVE:
|
|||
|
- conf_remove_now (node->section, node->tag);
|
|||
|
- break;
|
|||
|
- case CONF_REMOVE_SECTION:
|
|||
|
- conf_remove_section_now (node->section);
|
|||
|
- break;
|
|||
|
- default:
|
|||
|
- warnx("conf_end: unknown operation: %d", node->op);
|
|||
|
- }
|
|||
|
- TAILQ_REMOVE (&conf_trans_queue, node, link);
|
|||
|
- if (node->section)
|
|||
|
- free (node->section);
|
|||
|
- if (node->tag)
|
|||
|
- free (node->tag);
|
|||
|
- if (node->value)
|
|||
|
- free (node->value);
|
|||
|
- free (node);
|
|||
|
- }
|
|||
|
- }
|
|||
|
- return 0;
|
|||
|
-}
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * Dump running configuration upon SIGUSR1.
|
|||
|
- * Configuration is "stored in reverse order", so reverse it again.
|
|||
|
- */
|
|||
|
-struct dumper {
|
|||
|
- char *s, *v;
|
|||
|
- struct dumper *next;
|
|||
|
-};
|
|||
|
-
|
|||
|
-static void
|
|||
|
-conf_report_dump (struct dumper *node)
|
|||
|
-{
|
|||
|
- /* Recursive, cleanup when we're done. */
|
|||
|
-
|
|||
|
- if (node->next)
|
|||
|
- conf_report_dump (node->next);
|
|||
|
-
|
|||
|
- if (node->v)
|
|||
|
- warnx("%s=\t%s", node->s, node->v);
|
|||
|
- else if (node->s)
|
|||
|
- {
|
|||
|
- warnx("%s", node->s);
|
|||
|
- if (strlen (node->s) > 0)
|
|||
|
- free (node->s);
|
|||
|
- }
|
|||
|
-
|
|||
|
- free (node);
|
|||
|
-}
|
|||
|
-
|
|||
|
-void
|
|||
|
-conf_report (void)
|
|||
|
-{
|
|||
|
- struct conf_binding *cb, *last = 0;
|
|||
|
- unsigned int i, len;
|
|||
|
- char *current_section = (char *)0;
|
|||
|
- struct dumper *dumper, *dnode;
|
|||
|
-
|
|||
|
- dumper = dnode = (struct dumper *)calloc (1, sizeof *dumper);
|
|||
|
- if (!dumper)
|
|||
|
- goto mem_fail;
|
|||
|
-
|
|||
|
- warnx("conf_report: dumping running configuration");
|
|||
|
-
|
|||
|
- for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
|
|||
|
- for (cb = LIST_FIRST (&conf_bindings[i]); cb;
|
|||
|
- cb = LIST_NEXT (cb, link))
|
|||
|
- {
|
|||
|
- if (!cb->is_default)
|
|||
|
- {
|
|||
|
- /* Dump this entry. */
|
|||
|
- if (!current_section || strcmp (cb->section, current_section))
|
|||
|
- {
|
|||
|
- if (current_section)
|
|||
|
- {
|
|||
|
- len = strlen (current_section) + 3;
|
|||
|
- dnode->s = malloc (len);
|
|||
|
- if (!dnode->s)
|
|||
|
- goto mem_fail;
|
|||
|
-
|
|||
|
- snprintf (dnode->s, len, "[%s]", current_section);
|
|||
|
- dnode->next
|
|||
|
- = (struct dumper *)calloc (1, sizeof (struct dumper));
|
|||
|
- dnode = dnode->next;
|
|||
|
- if (!dnode)
|
|||
|
- goto mem_fail;
|
|||
|
-
|
|||
|
- dnode->s = "";
|
|||
|
- dnode->next
|
|||
|
- = (struct dumper *)calloc (1, sizeof (struct dumper));
|
|||
|
- dnode = dnode->next;
|
|||
|
- if (!dnode)
|
|||
|
- goto mem_fail;
|
|||
|
- }
|
|||
|
- current_section = cb->section;
|
|||
|
- }
|
|||
|
- dnode->s = cb->tag;
|
|||
|
- dnode->v = cb->value;
|
|||
|
- dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper));
|
|||
|
- dnode = dnode->next;
|
|||
|
- if (!dnode)
|
|||
|
- goto mem_fail;
|
|||
|
- last = cb;
|
|||
|
- }
|
|||
|
- }
|
|||
|
-
|
|||
|
- if (last)
|
|||
|
- {
|
|||
|
- len = strlen (last->section) + 3;
|
|||
|
- dnode->s = malloc (len);
|
|||
|
- if (!dnode->s)
|
|||
|
- goto mem_fail;
|
|||
|
- snprintf (dnode->s, len, "[%s]", last->section);
|
|||
|
- }
|
|||
|
-
|
|||
|
- conf_report_dump (dumper);
|
|||
|
-
|
|||
|
- return;
|
|||
|
-
|
|||
|
- mem_fail:
|
|||
|
- warnx("conf_report: malloc/calloc failed");
|
|||
|
- while ((dnode = dumper) != 0)
|
|||
|
- {
|
|||
|
- dumper = dumper->next;
|
|||
|
- if (dnode->s)
|
|||
|
- free (dnode->s);
|
|||
|
- free (dnode);
|
|||
|
- }
|
|||
|
- return;
|
|||
|
-}
|
|||
|
diff --git a/utils/idmapd/cfg.h b/utils/idmapd/cfg.h
|
|||
|
deleted file mode 100644
|
|||
|
index c1ca940..0000000
|
|||
|
--- a/utils/idmapd/cfg.h
|
|||
|
+++ /dev/null
|
|||
|
@@ -1,67 +0,0 @@
|
|||
|
-/* $OpenBSD: conf.h,v 1.30 2004/06/25 20:25:34 hshoexer Exp $ */
|
|||
|
-/* $EOM: conf.h,v 1.13 2000/09/18 00:01:47 ho Exp $ */
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist. All rights reserved.
|
|||
|
- * Copyright (c) 2000, 2003 H<>kan Olsson. All rights reserved.
|
|||
|
- *
|
|||
|
- * Redistribution and use in source and binary forms, with or without
|
|||
|
- * modification, are permitted provided that the following conditions
|
|||
|
- * are met:
|
|||
|
- * 1. Redistributions of source code must retain the above copyright
|
|||
|
- * notice, this list of conditions and the following disclaimer.
|
|||
|
- * 2. Redistributions in binary form must reproduce the above copyright
|
|||
|
- * notice, this list of conditions and the following disclaimer in the
|
|||
|
- * documentation and/or other materials provided with the distribution.
|
|||
|
- *
|
|||
|
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|||
|
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|||
|
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|||
|
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|||
|
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|||
|
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
|
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
|
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
|
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|||
|
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
- */
|
|||
|
-
|
|||
|
-/*
|
|||
|
- * This code was written under funding by Ericsson Radio Systems.
|
|||
|
- */
|
|||
|
-
|
|||
|
-#ifndef _CONF_H_
|
|||
|
-#define _CONF_H_
|
|||
|
-
|
|||
|
-#include "queue.h"
|
|||
|
-
|
|||
|
-struct conf_list_node {
|
|||
|
- TAILQ_ENTRY(conf_list_node) link;
|
|||
|
- char *field;
|
|||
|
-};
|
|||
|
-
|
|||
|
-struct conf_list {
|
|||
|
- size_t cnt;
|
|||
|
- TAILQ_HEAD(conf_list_fields_head, conf_list_node) fields;
|
|||
|
-};
|
|||
|
-
|
|||
|
-extern char *conf_path;
|
|||
|
-
|
|||
|
-extern int conf_begin(void);
|
|||
|
-extern int conf_decode_base64(u_int8_t *, u_int32_t *, u_char *);
|
|||
|
-extern int conf_end(int, int);
|
|||
|
-extern void conf_free_list(struct conf_list *);
|
|||
|
-extern struct sockaddr *conf_get_address(char *, char *);
|
|||
|
-extern struct conf_list *conf_get_list(char *, char *);
|
|||
|
-extern struct conf_list *conf_get_tag_list(char *);
|
|||
|
-extern int conf_get_num(char *, char *, int);
|
|||
|
-extern char *conf_get_str(char *, char *);
|
|||
|
-extern void conf_init(void);
|
|||
|
-extern int conf_match_num(char *, char *, int);
|
|||
|
-extern void conf_reinit(void);
|
|||
|
-extern int conf_remove(int, char *, char *);
|
|||
|
-extern int conf_remove_section(int, char *);
|
|||
|
-extern int conf_set(int, char *, char *, char *, int, int);
|
|||
|
-extern void conf_report(void);
|
|||
|
-
|
|||
|
-#endif /* _CONF_H_ */
|
|||
|
diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
|
|||
|
index 9cbe96c..65a6a2a 100644
|
|||
|
--- a/utils/idmapd/idmapd.c
|
|||
|
+++ b/utils/idmapd/idmapd.c
|
|||
|
@@ -66,7 +66,7 @@
|
|||
|
#endif /* HAVE_CONFIG_H */
|
|||
|
|
|||
|
#include "xlog.h"
|
|||
|
-#include "cfg.h"
|
|||
|
+#include "conffile.h"
|
|||
|
#include "queue.h"
|
|||
|
#include "nfslib.h"
|
|||
|
|
|||
|
@@ -156,7 +156,7 @@ static char *nobodyuser, *nobodygroup;
|
|||
|
static uid_t nobodyuid;
|
|||
|
static gid_t nobodygid;
|
|||
|
|
|||
|
-/* Used by cfg.c */
|
|||
|
+/* Used by conffile.c in libnfs.a */
|
|||
|
char *conf_path;
|
|||
|
|
|||
|
static int
|
|||
|
diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am
|
|||
|
index 459fa45..299384a 100644
|
|||
|
--- a/utils/mount/Makefile.am
|
|||
|
+++ b/utils/mount/Makefile.am
|
|||
|
@@ -15,7 +15,14 @@ mount_nfs_SOURCES = mount.c error.c network.c fstab.c token.c \
|
|||
|
nfsumount.c \
|
|||
|
mount_constants.h error.h network.h fstab.h token.h \
|
|||
|
parse_opt.h parse_dev.h \
|
|||
|
- nfs4_mount.h nfs_mount4.h stropts.h version.h
|
|||
|
+ nfs4_mount.h nfs_mount4.h stropts.h version.h \
|
|||
|
+ mount_config.h
|
|||
|
+
|
|||
|
+if MOUNT_CONFIG
|
|||
|
+mount_nfs_SOURCES += configfile.c
|
|||
|
+man5_MANS += nfsmount.conf.man
|
|||
|
+EXTRA_DIST += nfsmount.conf
|
|||
|
+endif
|
|||
|
|
|||
|
mount_nfs_LDADD = ../../support/nfs/libnfs.a \
|
|||
|
../../support/export/libexport.a
|
|||
|
diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c
|
|||
|
new file mode 100644
|
|||
|
index 0000000..e347b0e
|
|||
|
--- /dev/null
|
|||
|
+++ b/utils/mount/configfile.c
|
|||
|
@@ -0,0 +1,320 @@
|
|||
|
+/*
|
|||
|
+ * configfile.c -- mount configuration file manipulation
|
|||
|
+ * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com>
|
|||
|
+ *
|
|||
|
+ * - Routines use to create mount options from the mount
|
|||
|
+ * configuration file.
|
|||
|
+ *
|
|||
|
+ * This program 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.
|
|||
|
+ *
|
|||
|
+ * This program 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.
|
|||
|
+ *
|
|||
|
+ */
|
|||
|
+#ifdef HAVE_CONFIG_H
|
|||
|
+#include <config.h>
|
|||
|
+#endif
|
|||
|
+#include <sys/types.h>
|
|||
|
+
|
|||
|
+#include <stdio.h>
|
|||
|
+#include <stdlib.h>
|
|||
|
+#include <string.h>
|
|||
|
+#include <ctype.h>
|
|||
|
+
|
|||
|
+#include "xlog.h"
|
|||
|
+#include "conffile.h"
|
|||
|
+
|
|||
|
+#define KBYTES(x) ((x) * (1024))
|
|||
|
+#define MEGABYTES(x) ((x) * (1048576))
|
|||
|
+#define GIGABYTES(x) ((x) * (1073741824))
|
|||
|
+
|
|||
|
+#ifndef NFSMOUNT_GLOBAL_OPTS
|
|||
|
+#define NFSMOUNT_GLOBAL_OPTS "NFSMount_Global_Options"
|
|||
|
+#endif
|
|||
|
+
|
|||
|
+#ifndef NFSMOUNT_MOUNTPOINT
|
|||
|
+#define NFSMOUNT_MOUNTPOINT "MountPoint"
|
|||
|
+#endif
|
|||
|
+
|
|||
|
+#ifndef NFSMOUNT_SERVER
|
|||
|
+#define NFSMOUNT_SERVER "Server"
|
|||
|
+#endif
|
|||
|
+
|
|||
|
+#ifndef MOUNTOPTS_CONFFILE
|
|||
|
+#define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf"
|
|||
|
+#endif
|
|||
|
+char *conf_path = MOUNTOPTS_CONFFILE;
|
|||
|
+enum {
|
|||
|
+ MNT_NOARG=0,
|
|||
|
+ MNT_INTARG,
|
|||
|
+ MNT_STRARG,
|
|||
|
+ MNT_SPEC,
|
|||
|
+ MNT_UNSET
|
|||
|
+};
|
|||
|
+struct mnt_alias {
|
|||
|
+ char *alias;
|
|||
|
+ char *opt;
|
|||
|
+ int argtype;
|
|||
|
+} mnt_alias_tab[] = {
|
|||
|
+ {"background", "bg", MNT_NOARG},
|
|||
|
+ {"foreground", "fg", MNT_NOARG},
|
|||
|
+ {"sloppy", "sloppy", MNT_NOARG},
|
|||
|
+};
|
|||
|
+int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0]));
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * See if the option is an alias, if so return the
|
|||
|
+ * real mount option along with the argument type.
|
|||
|
+ */
|
|||
|
+inline static
|
|||
|
+char *mountopts_alias(char *opt, int *argtype)
|
|||
|
+{
|
|||
|
+ int i;
|
|||
|
+
|
|||
|
+ *argtype = MNT_UNSET;
|
|||
|
+ for (i=0; i < mnt_alias_sz; i++) {
|
|||
|
+ if (strcasecmp(opt, mnt_alias_tab[i].alias) != 0)
|
|||
|
+ continue;
|
|||
|
+ *argtype = mnt_alias_tab[i].argtype;
|
|||
|
+ return mnt_alias_tab[i].opt;
|
|||
|
+ }
|
|||
|
+ /* Make option names case-insensitive */
|
|||
|
+ upper2lower(opt);
|
|||
|
+
|
|||
|
+ return opt;
|
|||
|
+}
|
|||
|
+/*
|
|||
|
+ * Convert numeric strings that end with 'k', 'm' or 'g'
|
|||
|
+ * into numeric strings with the real value.
|
|||
|
+ * Meaning '8k' becomes '8094'.
|
|||
|
+ */
|
|||
|
+char *mountopts_convert(char *value)
|
|||
|
+{
|
|||
|
+ unsigned long long factor, num;
|
|||
|
+ static char buf[64];
|
|||
|
+ char *ch;
|
|||
|
+
|
|||
|
+ ch = &value[strlen(value)-1];
|
|||
|
+ switch (tolower(*ch)) {
|
|||
|
+ case 'k':
|
|||
|
+ factor = KBYTES(1);
|
|||
|
+ break;
|
|||
|
+ case 'm':
|
|||
|
+ factor = MEGABYTES(1);
|
|||
|
+ break;
|
|||
|
+ case 'g':
|
|||
|
+ factor = GIGABYTES(1);
|
|||
|
+ break;
|
|||
|
+ default:
|
|||
|
+ return value;
|
|||
|
+ }
|
|||
|
+ *ch = '\0';
|
|||
|
+ if (strncmp(value, "0x", 2) == 0) {
|
|||
|
+ num = strtol(value, (char **)NULL, 16);
|
|||
|
+ } else if (strncmp(value, "0", 1) == 0) {
|
|||
|
+ num = strtol(value, (char **)NULL, 8);
|
|||
|
+ } else {
|
|||
|
+ num = strtol(value, (char **)NULL, 10);
|
|||
|
+ }
|
|||
|
+ num *= factor;
|
|||
|
+ snprintf(buf, 64, "%lld", num);
|
|||
|
+
|
|||
|
+ return buf;
|
|||
|
+}
|
|||
|
+
|
|||
|
+struct entry {
|
|||
|
+ SLIST_ENTRY(entry) entries;
|
|||
|
+ char *opt;
|
|||
|
+};
|
|||
|
+static SLIST_HEAD(shead, entry) head = SLIST_HEAD_INITIALIZER(head);
|
|||
|
+static int list_size;
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Add option to the link list
|
|||
|
+ */
|
|||
|
+inline static void
|
|||
|
+add_entry(char *opt)
|
|||
|
+{
|
|||
|
+ struct entry *entry;
|
|||
|
+
|
|||
|
+ entry = calloc(1, sizeof(struct entry));
|
|||
|
+ if (entry == NULL) {
|
|||
|
+ xlog_warn("Unable calloc memory for mount configs");
|
|||
|
+ return;
|
|||
|
+ }
|
|||
|
+ entry->opt = strdup(opt);
|
|||
|
+ if (entry->opt == NULL) {
|
|||
|
+ xlog_warn("Unable calloc memory for mount opts");
|
|||
|
+ free(entry);
|
|||
|
+ return;
|
|||
|
+ }
|
|||
|
+ SLIST_INSERT_HEAD(&head, entry, entries);
|
|||
|
+}
|
|||
|
+/*
|
|||
|
+ * See if the given entry exists if the link list,
|
|||
|
+ * if so return that entry
|
|||
|
+ */
|
|||
|
+inline static
|
|||
|
+char *lookup_entry(char *opt)
|
|||
|
+{
|
|||
|
+ struct entry *entry;
|
|||
|
+
|
|||
|
+ SLIST_FOREACH(entry, &head, entries) {
|
|||
|
+ if (strcasecmp(entry->opt, opt) == 0)
|
|||
|
+ return opt;
|
|||
|
+ }
|
|||
|
+ return NULL;
|
|||
|
+}
|
|||
|
+/*
|
|||
|
+ * Free all entries on the link list
|
|||
|
+ */
|
|||
|
+inline static
|
|||
|
+void free_all(void)
|
|||
|
+{
|
|||
|
+ struct entry *entry;
|
|||
|
+
|
|||
|
+ while (!SLIST_EMPTY(&head)) {
|
|||
|
+ entry = SLIST_FIRST(&head);
|
|||
|
+ SLIST_REMOVE_HEAD(&head, entries);
|
|||
|
+ free(entry->opt);
|
|||
|
+ free(entry);
|
|||
|
+ }
|
|||
|
+}
|
|||
|
+/*
|
|||
|
+ * Parse the given section of the configuration
|
|||
|
+ * file to if there are any mount options set.
|
|||
|
+ * If so, added them to link list.
|
|||
|
+ */
|
|||
|
+static void
|
|||
|
+conf_parse_mntopts(char *section, char *arg, char *opts)
|
|||
|
+{
|
|||
|
+ struct conf_list *list;
|
|||
|
+ struct conf_list_node *node;
|
|||
|
+ char buf[BUFSIZ], *value, *field;
|
|||
|
+ char *nvalue, *ptr;
|
|||
|
+ int argtype;
|
|||
|
+
|
|||
|
+ list = conf_get_tag_list(section);
|
|||
|
+ TAILQ_FOREACH(node, &list->fields, link) {
|
|||
|
+ /*
|
|||
|
+ * Do not overwrite options if already exists
|
|||
|
+ */
|
|||
|
+ snprintf(buf, BUFSIZ, "%s=", node->field);
|
|||
|
+ if (opts && strcasestr(opts, buf) != NULL)
|
|||
|
+ continue;
|
|||
|
+ if (lookup_entry(node->field) != NULL)
|
|||
|
+ continue;
|
|||
|
+ buf[0] = '\0';
|
|||
|
+ value = conf_get_section(section, arg, node->field);
|
|||
|
+ if (value == NULL)
|
|||
|
+ continue;
|
|||
|
+ field = mountopts_alias(node->field, &argtype);
|
|||
|
+ if (strcasecmp(value, "false") == 0) {
|
|||
|
+ if (argtype != MNT_NOARG)
|
|||
|
+ snprintf(buf, BUFSIZ, "no%s", field);
|
|||
|
+ } else if (strcasecmp(value, "true") == 0) {
|
|||
|
+ snprintf(buf, BUFSIZ, "%s", field);
|
|||
|
+ } else {
|
|||
|
+ nvalue = strdup(value);
|
|||
|
+ ptr = mountopts_convert(nvalue);
|
|||
|
+ snprintf(buf, BUFSIZ, "%s=%s", field, ptr);
|
|||
|
+ free(nvalue);
|
|||
|
+ }
|
|||
|
+ if (buf[0] == '\0')
|
|||
|
+ continue;
|
|||
|
+ /*
|
|||
|
+ * Keep a running tally of the list size adding
|
|||
|
+ * one for the ',' that will be appened later
|
|||
|
+ */
|
|||
|
+ list_size += strlen(buf) + 1;
|
|||
|
+ add_entry(buf);
|
|||
|
+ }
|
|||
|
+ conf_free_list(list);
|
|||
|
+}
|
|||
|
+
|
|||
|
+/*
|
|||
|
+ * Concatenate options from the configuration file with the
|
|||
|
+ * given options by building a link list of options from the
|
|||
|
+ * different sections in the conf file. Options that exists
|
|||
|
+ * in the either the given options or link list are not
|
|||
|
+ * overwritten so it matter which when each section is
|
|||
|
+ * parsed.
|
|||
|
+ */
|
|||
|
+char *conf_get_mntopts(char *spec, char *mount_point,
|
|||
|
+ char *mount_opts)
|
|||
|
+{
|
|||
|
+ struct entry *entry;
|
|||
|
+ char *ptr, *server, *config_opts;
|
|||
|
+ int optlen = 0;
|
|||
|
+
|
|||
|
+ SLIST_INIT(&head);
|
|||
|
+ list_size = 0;
|
|||
|
+ /*
|
|||
|
+ * First see if there are any mount options relative
|
|||
|
+ * to the mount point.
|
|||
|
+ */
|
|||
|
+ conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, mount_opts);
|
|||
|
+
|
|||
|
+ /*
|
|||
|
+ * Next, see if there are any mount options relative
|
|||
|
+ * to the server
|
|||
|
+ */
|
|||
|
+ server = strdup(spec);
|
|||
|
+ if (server == NULL) {
|
|||
|
+ xlog_warn("conf_get_mountops: Unable calloc memory for server");
|
|||
|
+ free_all();
|
|||
|
+ return mount_opts;
|
|||
|
+ }
|
|||
|
+ if ((ptr = strchr(server, ':')) != NULL)
|
|||
|
+ *ptr='\0';
|
|||
|
+ conf_parse_mntopts(NFSMOUNT_SERVER, server, mount_opts);
|
|||
|
+ free(server);
|
|||
|
+
|
|||
|
+ /*
|
|||
|
+ * Finally process all the global mount options.
|
|||
|
+ */
|
|||
|
+ conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts);
|
|||
|
+
|
|||
|
+ /*
|
|||
|
+ * If no mount options were found in the configuration file
|
|||
|
+ * just return what was passed in .
|
|||
|
+ */
|
|||
|
+ if (SLIST_EMPTY(&head))
|
|||
|
+ return mount_opts;
|
|||
|
+
|
|||
|
+ /*
|
|||
|
+ * Found options in the configuration file. So
|
|||
|
+ * concatenate the configuration options with the
|
|||
|
+ * options that were passed in
|
|||
|
+ */
|
|||
|
+ if (mount_opts)
|
|||
|
+ optlen = strlen(mount_opts);
|
|||
|
+
|
|||
|
+ /* list_size + optlen + ',' + '\0' */
|
|||
|
+ config_opts = calloc(1, (list_size+optlen+2));
|
|||
|
+ if (server == NULL) {
|
|||
|
+ xlog_warn("conf_get_mountops: Unable calloc memory for config_opts");
|
|||
|
+ free_all();
|
|||
|
+ return mount_opts;
|
|||
|
+ }
|
|||
|
+ if (mount_opts) {
|
|||
|
+ strcpy(config_opts, mount_opts);
|
|||
|
+ strcat(config_opts, ",");
|
|||
|
+ }
|
|||
|
+ SLIST_FOREACH(entry, &head, entries) {
|
|||
|
+ strcat(config_opts, entry->opt);
|
|||
|
+ strcat(config_opts, ",");
|
|||
|
+ }
|
|||
|
+ *(strrchr(config_opts, ',')) = '\0';
|
|||
|
+
|
|||
|
+ free_all();
|
|||
|
+ if (mount_opts)
|
|||
|
+ free(mount_opts);
|
|||
|
+
|
|||
|
+ return config_opts;
|
|||
|
+}
|
|||
|
diff --git a/utils/mount/mount.c b/utils/mount/mount.c
|
|||
|
index a668cd9..355df79 100644
|
|||
|
--- a/utils/mount/mount.c
|
|||
|
+++ b/utils/mount/mount.c
|
|||
|
@@ -37,6 +37,7 @@
|
|||
|
#include "xcommon.h"
|
|||
|
#include "nls.h"
|
|||
|
#include "mount_constants.h"
|
|||
|
+#include "mount_config.h"
|
|||
|
#include "nfs_paths.h"
|
|||
|
#include "nfs_mntent.h"
|
|||
|
|
|||
|
@@ -474,6 +475,8 @@ int main(int argc, char *argv[])
|
|||
|
spec = argv[1];
|
|||
|
mount_point = argv[2];
|
|||
|
|
|||
|
+ mount_config_init(progname);
|
|||
|
+
|
|||
|
argv[2] = argv[0]; /* so that getopt error messages are correct */
|
|||
|
while ((c = getopt_long(argc - 2, argv + 2, "rvVwfno:hs",
|
|||
|
longopts, NULL)) != -1) {
|
|||
|
@@ -559,6 +562,10 @@ int main(int argc, char *argv[])
|
|||
|
mnt_err = EX_USAGE;
|
|||
|
goto out;
|
|||
|
}
|
|||
|
+ /*
|
|||
|
+ * Concatenate mount options from the configuration file
|
|||
|
+ */
|
|||
|
+ mount_opts = mount_config_opts(spec, mount_point, mount_opts);
|
|||
|
|
|||
|
parse_opts(mount_opts, &flags, &extra_opts);
|
|||
|
|
|||
|
diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h
|
|||
|
new file mode 100644
|
|||
|
index 0000000..9a885a9
|
|||
|
--- /dev/null
|
|||
|
+++ b/utils/mount/mount_config.h
|
|||
|
@@ -0,0 +1,48 @@
|
|||
|
+#ifndef _LINUX_MOUNT__CONFIG_H
|
|||
|
+#define _LINUX_MOUNT_CONFIG__H
|
|||
|
+/*
|
|||
|
+ * mount_config.h -- mount configuration file routines
|
|||
|
+ * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com>
|
|||
|
+ *
|
|||
|
+ * This program 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.
|
|||
|
+ *
|
|||
|
+ * This program 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.
|
|||
|
+ *
|
|||
|
+ */
|
|||
|
+
|
|||
|
+inline void mount_config_init(char *);
|
|||
|
+
|
|||
|
+#ifdef MOUNT_CONFIG
|
|||
|
+#include "conffile.h"
|
|||
|
+extern char *conf_get_mntopts(char *, char *, char *);
|
|||
|
+
|
|||
|
+inline void mount_config_init(char *program)
|
|||
|
+{
|
|||
|
+ xlog_open(program);
|
|||
|
+ /*
|
|||
|
+ * Read the the default mount options
|
|||
|
+ */
|
|||
|
+ conf_init();
|
|||
|
+}
|
|||
|
+inline char *mount_config_opts(char *spec,
|
|||
|
+ char *mount_point, char *mount_opts)
|
|||
|
+{
|
|||
|
+ return conf_get_mntopts(spec, mount_point, mount_opts);
|
|||
|
+}
|
|||
|
+#else /* MOUNT_CONFIG */
|
|||
|
+
|
|||
|
+inline void mount_config_init(char *program) { }
|
|||
|
+
|
|||
|
+inline char *mount_config_opts(char *spec,
|
|||
|
+ char *mount_point, char *mount_opts)
|
|||
|
+{
|
|||
|
+ return mount_opts;
|
|||
|
+}
|
|||
|
+#endif /* MOUNT_CONFIG */
|
|||
|
+#endif
|
|||
|
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
|
|||
|
index 13de524..2299637 100644
|
|||
|
--- a/utils/mount/nfs.man
|
|||
|
+++ b/utils/mount/nfs.man
|
|||
|
@@ -28,7 +28,7 @@ The
|
|||
|
file describes how
|
|||
|
.BR mount (8)
|
|||
|
should assemble a system's file name hierarchy
|
|||
|
-from various independent file systems
|
|||
|
+from various independent file systems
|
|||
|
(including file systems exported by NFS servers).
|
|||
|
Each line in the
|
|||
|
.I /etc/fstab
|
|||
|
@@ -56,7 +56,7 @@ by NFS, thus conventionally each contain the digit zero. For example:
|
|||
|
.P
|
|||
|
The server's hostname and export pathname
|
|||
|
are separated by a colon, while
|
|||
|
-the mount options are separated by commas. The remaining fields
|
|||
|
+the mount options are separated by commas. The remaining fields
|
|||
|
are separated by blanks or tabs.
|
|||
|
The server's hostname can be an unqualified hostname,
|
|||
|
a fully qualified domain name,
|
|||
|
@@ -70,17 +70,16 @@ The
|
|||
|
and
|
|||
|
.B nfs4
|
|||
|
file system types share similar mount options,
|
|||
|
-which are described below.
|
|||
|
+which are described below.
|
|||
|
.SH "MOUNT OPTIONS"
|
|||
|
-Refer to
|
|||
|
+Refer to
|
|||
|
.BR mount (8)
|
|||
|
for a description of generic mount options
|
|||
|
-available for all file systems. If you do not need to
|
|||
|
-specify any mount options, use the generic option
|
|||
|
+available for all file systems. If you do not need to
|
|||
|
+specify any mount options, use the generic option
|
|||
|
.B defaults
|
|||
|
in
|
|||
|
.IR /etc/fstab .
|
|||
|
-.
|
|||
|
.DT
|
|||
|
.SS "Valid options for either the nfs or nfs4 file system type"
|
|||
|
These options are valid to use when mounting either
|
|||
|
@@ -100,7 +99,7 @@ option is specified), NFS requests are retried indefinitely.
|
|||
|
If the
|
|||
|
.B soft
|
|||
|
option is specified, then the NFS client fails an NFS request
|
|||
|
-after
|
|||
|
+after
|
|||
|
.B retrans
|
|||
|
retransmissions have been sent,
|
|||
|
causing the NFS client to return an error
|
|||
|
@@ -119,8 +118,8 @@ option may mitigate some of the risks of using the
|
|||
|
option.
|
|||
|
.TP 1.5i
|
|||
|
.BI timeo= n
|
|||
|
-The time (in tenths of a second) the NFS client waits for a
|
|||
|
-response before it retries an NFS request. If this
|
|||
|
+The time (in tenths of a second) the NFS client waits for a
|
|||
|
+response before it retries an NFS request. If this
|
|||
|
option is not specified, requests are retried every
|
|||
|
60 seconds for NFS over TCP.
|
|||
|
The NFS client does not perform any kind of timeout backoff
|
|||
|
@@ -128,7 +127,7 @@ for NFS over TCP.
|
|||
|
.IP
|
|||
|
However, for NFS over UDP, the client uses an adaptive
|
|||
|
algorithm to estimate an appropriate timeout value for frequently used
|
|||
|
-request types (such as READ and WRITE requests), but uses the
|
|||
|
+request types (such as READ and WRITE requests), but uses the
|
|||
|
.B timeo
|
|||
|
setting for infrequently used request types (such as FSINFO requests).
|
|||
|
If the
|
|||
|
@@ -141,13 +140,13 @@ up to a maximum timeout length of 60 seconds.
|
|||
|
.TP 1.5i
|
|||
|
.BI retrans= n
|
|||
|
The number of times the NFS client retries a request before
|
|||
|
-it attempts further recovery action. If the
|
|||
|
+it attempts further recovery action. If the
|
|||
|
.B retrans
|
|||
|
-option is not specified, the NFS client tries each request
|
|||
|
+option is not specified, the NFS client tries each request
|
|||
|
three times.
|
|||
|
.IP
|
|||
|
The NFS client generates a "server not responding" message
|
|||
|
-after
|
|||
|
+after
|
|||
|
.B retrans
|
|||
|
retries, then attempts further recovery (depending on whether the
|
|||
|
.B hard
|
|||
|
@@ -166,21 +165,21 @@ is 1,048,576 bytes (one megabyte).
|
|||
|
The
|
|||
|
.B rsize
|
|||
|
value is a positive integral multiple of 1024.
|
|||
|
-Specified
|
|||
|
+Specified
|
|||
|
.B rsize
|
|||
|
values lower than 1024 are replaced with 4096; values larger than
|
|||
|
1048576 are replaced with 1048576. If a specified value is within the supported
|
|||
|
-range but not a multiple of 1024, it is rounded down to the nearest
|
|||
|
+range but not a multiple of 1024, it is rounded down to the nearest
|
|||
|
multiple of 1024.
|
|||
|
.IP
|
|||
|
If an
|
|||
|
.B rsize
|
|||
|
-value is not specified, or if the specified
|
|||
|
-.B rsize
|
|||
|
+value is not specified, or if the specified
|
|||
|
+.B rsize
|
|||
|
value is larger than the maximum that either client or server can support,
|
|||
|
the client and server negotiate the largest
|
|||
|
.B rsize
|
|||
|
-value that they can both support.
|
|||
|
+value that they can both support.
|
|||
|
.IP
|
|||
|
The
|
|||
|
.B rsize
|
|||
|
@@ -197,7 +196,7 @@ file.
|
|||
|
.BI wsize= n
|
|||
|
The maximum number of bytes per network WRITE request
|
|||
|
that the NFS client can send when writing data to a file
|
|||
|
-on an NFS server. The actual data payload size of each
|
|||
|
+on an NFS server. The actual data payload size of each
|
|||
|
NFS WRITE request is equal to
|
|||
|
or smaller than the
|
|||
|
.B wsize
|
|||
|
@@ -207,19 +206,19 @@ is 1,048,576 bytes (one megabyte).
|
|||
|
Similar to
|
|||
|
.B rsize
|
|||
|
, the
|
|||
|
-.B wsize
|
|||
|
+.B wsize
|
|||
|
value is a positive integral multiple of 1024.
|
|||
|
-Specified
|
|||
|
+Specified
|
|||
|
.B wsize
|
|||
|
values lower than 1024 are replaced with 4096; values larger than
|
|||
|
1048576 are replaced with 1048576. If a specified value is within the supported
|
|||
|
-range but not a multiple of 1024, it is rounded down to the nearest
|
|||
|
+range but not a multiple of 1024, it is rounded down to the nearest
|
|||
|
multiple of 1024.
|
|||
|
.IP
|
|||
|
If a
|
|||
|
.B wsize
|
|||
|
-value is not specified, or if the specified
|
|||
|
-.B wsize
|
|||
|
+value is not specified, or if the specified
|
|||
|
+.B wsize
|
|||
|
value is larger than the maximum that either client or server can support,
|
|||
|
the client and server negotiate the largest
|
|||
|
.B wsize
|
|||
|
@@ -235,31 +234,31 @@ file. However, the effective
|
|||
|
.B wsize
|
|||
|
value negotiated by the client and server is reported in the
|
|||
|
.I /proc/mounts
|
|||
|
-file.
|
|||
|
+file.
|
|||
|
.TP 1.5i
|
|||
|
.BR ac " / " noac
|
|||
|
-Selects whether the client may cache file attributes. If neither
|
|||
|
-option is specified (or if
|
|||
|
+Selects whether the client may cache file attributes. If neither
|
|||
|
+option is specified (or if
|
|||
|
.B ac
|
|||
|
-is specified), the client caches file
|
|||
|
-attributes.
|
|||
|
+is specified), the client caches file
|
|||
|
+attributes.
|
|||
|
.IP
|
|||
|
-To improve performance, NFS clients cache file
|
|||
|
-attributes. Every few seconds, an NFS client checks the server's version of each
|
|||
|
-file's attributes for updates. Changes that occur on the server in
|
|||
|
-those small intervals remain undetected until the client checks the
|
|||
|
-server again. The
|
|||
|
+To improve performance, NFS clients cache file
|
|||
|
+attributes. Every few seconds, an NFS client checks the server's version of each
|
|||
|
+file's attributes for updates. Changes that occur on the server in
|
|||
|
+those small intervals remain undetected until the client checks the
|
|||
|
+server again. The
|
|||
|
.B noac
|
|||
|
-option prevents clients from caching file
|
|||
|
-attributes so that applications can more quickly detect file changes
|
|||
|
+option prevents clients from caching file
|
|||
|
+attributes so that applications can more quickly detect file changes
|
|||
|
on the server.
|
|||
|
.IP
|
|||
|
-In addition to preventing the client from caching file attributes,
|
|||
|
-the
|
|||
|
+In addition to preventing the client from caching file attributes,
|
|||
|
+the
|
|||
|
.B noac
|
|||
|
-option forces application writes to become synchronous so
|
|||
|
-that local changes to a file become visible on the server
|
|||
|
-immediately. That way, other clients can quickly detect recent
|
|||
|
+option forces application writes to become synchronous so
|
|||
|
+that local changes to a file become visible on the server
|
|||
|
+immediately. That way, other clients can quickly detect recent
|
|||
|
writes when they check the file's attributes.
|
|||
|
.IP
|
|||
|
Using the
|
|||
|
@@ -383,20 +382,20 @@ Refer to the SECURITY CONSIDERATIONS section for details.
|
|||
|
.TP 1.5i
|
|||
|
.BR sharecache " / " nosharecache
|
|||
|
Determines how the client's data cache and attribute cache are shared
|
|||
|
-when mounting the same export more than once concurrently. Using the
|
|||
|
-same cache reduces memory requirements on the client and presents
|
|||
|
-identical file contents to applications when the same remote file is
|
|||
|
+when mounting the same export more than once concurrently. Using the
|
|||
|
+same cache reduces memory requirements on the client and presents
|
|||
|
+identical file contents to applications when the same remote file is
|
|||
|
accessed via different mount points.
|
|||
|
.IP
|
|||
|
-If neither option is specified, or if the
|
|||
|
+If neither option is specified, or if the
|
|||
|
.B sharecache
|
|||
|
-option is
|
|||
|
-specified, then a single cache is used for all mount points that
|
|||
|
-access the same export. If the
|
|||
|
+option is
|
|||
|
+specified, then a single cache is used for all mount points that
|
|||
|
+access the same export. If the
|
|||
|
.B nosharecache
|
|||
|
-option is specified,
|
|||
|
-then that mount point gets a unique cache. Note that when data and
|
|||
|
-attribute caches are shared, the mount options from the first mount
|
|||
|
+option is specified,
|
|||
|
+then that mount point gets a unique cache. Note that when data and
|
|||
|
+attribute caches are shared, the mount options from the first mount
|
|||
|
point take effect for subsequent concurrent mounts of the same export.
|
|||
|
.IP
|
|||
|
As of kernel 2.6.18, the behavior specified by
|
|||
|
@@ -422,6 +421,49 @@ NFS mount points allowed on a client, but NFS servers must be configured
|
|||
|
to allow clients to connect via non-privileged source ports.
|
|||
|
.IP
|
|||
|
Refer to the SECURITY CONSIDERATIONS section for important details.
|
|||
|
+.TP 1.5i
|
|||
|
+.BI lookupcache= mode
|
|||
|
+Specifies how the kernel manages its cache of directory entries
|
|||
|
+for a given mount point.
|
|||
|
+.I mode
|
|||
|
+can be one of
|
|||
|
+.BR all ,
|
|||
|
+.BR none ,
|
|||
|
+.BR pos ,
|
|||
|
+or
|
|||
|
+.BR positive .
|
|||
|
+This option is supported in kernels 2.6.28 and later.
|
|||
|
+.IP
|
|||
|
+The Linux NFS client caches the result of all NFS LOOKUP requests.
|
|||
|
+If the requested directory entry exists on the server,
|
|||
|
+the result is referred to as
|
|||
|
+.IR positive .
|
|||
|
+If the requested directory entry does not exist on the server,
|
|||
|
+the result is referred to as
|
|||
|
+.IR negative .
|
|||
|
+.IP
|
|||
|
+If this option is not specified, or if
|
|||
|
+.B all
|
|||
|
+is specified, the client assumes both types of directory cache entries
|
|||
|
+are valid until their parent directory's cached attributes expire.
|
|||
|
+.IP
|
|||
|
+If
|
|||
|
+.BR pos " or " positive
|
|||
|
+is specified, the client assumes positive entries are valid
|
|||
|
+until their parent directory's cached attributes expire, but
|
|||
|
+always revalidates negative entires before an application
|
|||
|
+can use them.
|
|||
|
+.IP
|
|||
|
+If
|
|||
|
+.B none
|
|||
|
+is specified,
|
|||
|
+the client revalidates both types of directory cache entries
|
|||
|
+before an application can use them.
|
|||
|
+This permits quick detection of files that were created or removed
|
|||
|
+by other clients, but can impact application and server performance.
|
|||
|
+.IP
|
|||
|
+The DATA AND METADATA COHERENCE section contains a
|
|||
|
+detailed discussion of these trade-offs.
|
|||
|
.SS "Valid options for the nfs file system type"
|
|||
|
Use these options, along with the options in the above subsection,
|
|||
|
for mounting the
|
|||
|
@@ -446,9 +488,9 @@ In addition to controlling how the NFS client transmits requests to
|
|||
|
the server, this mount option also controls how the
|
|||
|
.BR mount (8)
|
|||
|
command communicates with the server's rpcbind and mountd services.
|
|||
|
-Specifying
|
|||
|
+Specifying
|
|||
|
.B proto=tcp
|
|||
|
-forces all traffic from the
|
|||
|
+forces all traffic from the
|
|||
|
.BR mount (8)
|
|||
|
command and the NFS client to use TCP.
|
|||
|
Specifying
|
|||
|
@@ -574,9 +616,9 @@ It is included for compatibility with other operating systems.
|
|||
|
.TP 1.5i
|
|||
|
.BR lock " / " nolock
|
|||
|
Selects whether to use the NLM sideband protocol to lock files on the server.
|
|||
|
-If neither option is specified (or if
|
|||
|
-.B lock
|
|||
|
-is specified), NLM locking is used for this mount point.
|
|||
|
+If neither option is specified (or if
|
|||
|
+.B lock
|
|||
|
+is specified), NLM locking is used for this mount point.
|
|||
|
When using the
|
|||
|
.B nolock
|
|||
|
option, applications can lock files,
|
|||
|
@@ -598,13 +640,13 @@ that do not support the NLM protocol.
|
|||
|
.TP 1.5i
|
|||
|
.BR intr " / " nointr
|
|||
|
Selects whether to allow signals to interrupt file operations
|
|||
|
-on this mount point. If neither option
|
|||
|
-is specified (or if
|
|||
|
+on this mount point. If neither option
|
|||
|
+is specified (or if
|
|||
|
.B nointr
|
|||
|
is specified),
|
|||
|
signals do not interrupt NFS file operations. If
|
|||
|
-.B intr
|
|||
|
-is specified, system calls return EINTR if an in-progress NFS operation is interrupted by
|
|||
|
+.B intr
|
|||
|
+is specified, system calls return EINTR if an in-progress NFS operation is interrupted by
|
|||
|
a signal.
|
|||
|
.IP
|
|||
|
Using the
|
|||
|
@@ -622,13 +664,13 @@ compatibility with older kernels.
|
|||
|
.TP 1.5i
|
|||
|
.BR cto " / " nocto
|
|||
|
Selects whether to use close-to-open cache coherence semantics.
|
|||
|
-If neither option is specified (or if
|
|||
|
+If neither option is specified (or if
|
|||
|
.B cto
|
|||
|
is specified), the client uses close-to-open
|
|||
|
-cache coherence semantics. If the
|
|||
|
-.B nocto
|
|||
|
+cache coherence semantics. If the
|
|||
|
+.B nocto
|
|||
|
option is specified, the client uses a non-standard heuristic to determine when
|
|||
|
-files on the server have changed.
|
|||
|
+files on the server have changed.
|
|||
|
.IP
|
|||
|
Using the
|
|||
|
.B nocto
|
|||
|
@@ -640,13 +682,13 @@ of this option in more detail.
|
|||
|
.BR acl " / " noacl
|
|||
|
Selects whether to use the NFSACL sideband protocol on this mount point.
|
|||
|
The NFSACL sideband protocol is a proprietary protocol
|
|||
|
-implemented in Solaris that manages Access Control Lists. NFSACL was never
|
|||
|
+implemented in Solaris that manages Access Control Lists. NFSACL was never
|
|||
|
made a standard part of the NFS protocol specification.
|
|||
|
.IP
|
|||
|
-If neither
|
|||
|
+If neither
|
|||
|
.B acl
|
|||
|
-nor
|
|||
|
-.B noacl
|
|||
|
+nor
|
|||
|
+.B noacl
|
|||
|
option is specified,
|
|||
|
the NFS client negotiates with the server
|
|||
|
to see if the NFSACL protocol is supported,
|
|||
|
@@ -660,7 +702,7 @@ Selects whether to use NFS version 3 READDIRPLUS requests.
|
|||
|
If this option is not specified, the NFS client uses READDIRPLUS requests
|
|||
|
on NFS version 3 mounts to read small directories.
|
|||
|
Some applications perform better if the client uses only READDIR requests
|
|||
|
-for all directories.
|
|||
|
+for all directories.
|
|||
|
.SS "Valid options for the nfs4 file system type"
|
|||
|
Use these options, along with the options in the first subsection above,
|
|||
|
for mounting the
|
|||
|
@@ -676,8 +718,8 @@ can be either
|
|||
|
or
|
|||
|
.BR tcp .
|
|||
|
All NFS version 4 servers are required to support TCP,
|
|||
|
-so if this mount option is not specified, the NFS version 4 client
|
|||
|
-uses the TCP transport protocol.
|
|||
|
+so if this mount option is not specified, the NFS version 4 client
|
|||
|
+uses the TCP transport protocol.
|
|||
|
Refer to the TRANSPORT METHODS section for more details.
|
|||
|
.TP 1.5i
|
|||
|
.BI port= n
|
|||
|
@@ -700,12 +742,12 @@ or the server's NFS service is not available on the advertised port.
|
|||
|
.TP 1.5i
|
|||
|
.BR intr " / " nointr
|
|||
|
Selects whether to allow signals to interrupt file operations
|
|||
|
-on this mount point. If neither option is specified (or if
|
|||
|
-.B intr
|
|||
|
-is specified), system calls return EINTR if an in-progress NFS operation
|
|||
|
-is interrupted by a signal. If
|
|||
|
+on this mount point. If neither option is specified (or if
|
|||
|
+.B intr
|
|||
|
+is specified), system calls return EINTR if an in-progress NFS operation
|
|||
|
+is interrupted by a signal. If
|
|||
|
.B nointr
|
|||
|
-is specified, signals do not
|
|||
|
+is specified, signals do not
|
|||
|
interrupt NFS operations.
|
|||
|
.IP
|
|||
|
Using the
|
|||
|
@@ -727,7 +769,7 @@ for NFS directories on this mount point.
|
|||
|
If neither
|
|||
|
.B cto
|
|||
|
nor
|
|||
|
-.B nocto
|
|||
|
+.B nocto
|
|||
|
is specified,
|
|||
|
the default is to use close-to-open cache coherence
|
|||
|
semantics for directories.
|
|||
|
@@ -737,11 +779,11 @@ The DATA AND METADATA COHERENCE section discusses
|
|||
|
the behavior of this option in more detail.
|
|||
|
.TP 1.5i
|
|||
|
.BI clientaddr= n.n.n.n
|
|||
|
-Specifies a single IPv4 address (in dotted-quad form)
|
|||
|
-that the NFS client advertises to allow servers
|
|||
|
-to perform NFS version 4 callback requests against
|
|||
|
-files on this mount point. If the server is unable to
|
|||
|
-establish callback connections to clients, performance
|
|||
|
+Specifies a single IPv4 address (in dotted-quad form)
|
|||
|
+that the NFS client advertises to allow servers
|
|||
|
+to perform NFS version 4 callback requests against
|
|||
|
+files on this mount point. If the server is unable to
|
|||
|
+establish callback connections to clients, performance
|
|||
|
may degrade, or accesses to files may temporarily hang.
|
|||
|
.IP
|
|||
|
If this option is not specified, the
|
|||
|
@@ -751,7 +793,14 @@ The automatic discovery process is not perfect, however.
|
|||
|
In the presence of multiple client network interfaces,
|
|||
|
special routing policies,
|
|||
|
or atypical network topologies,
|
|||
|
-the exact address to use for callbacks may be nontrivial to determine.
|
|||
|
+the exact address to use for callbacks may be nontrivial to determine.
|
|||
|
+.SH MOUNT CONFIGURATION FILE
|
|||
|
+If the mount command is configured to do so, all of the mount options
|
|||
|
+described in the previous section can also be configured in the
|
|||
|
+.I /etc/nfsmount.conf
|
|||
|
+file. See
|
|||
|
+.BR nfsmount.conf(5)
|
|||
|
+for details.
|
|||
|
.SH EXAMPLES
|
|||
|
To mount an export using NFS version 2,
|
|||
|
use the
|
|||
|
@@ -824,33 +873,33 @@ and data transfer size settings for a mount point.
|
|||
|
In some cases, however, it pays to specify
|
|||
|
these settings explicitly using mount options.
|
|||
|
.P
|
|||
|
-Traditionally, NFS clients used the UDP transport exclusively for
|
|||
|
-transmitting requests to servers. Though its implementation is
|
|||
|
-simple, NFS over UDP has many limitations that prevent smooth
|
|||
|
-operation and good performance in some common deployment
|
|||
|
-environments. Even an insignificant packet loss rate results in the
|
|||
|
-loss of whole NFS requests; as such, retransmit timeouts are usually
|
|||
|
-in the subsecond range to allow clients to recover quickly from
|
|||
|
-dropped requests, but this can result in extraneous network traffic
|
|||
|
+Traditionally, NFS clients used the UDP transport exclusively for
|
|||
|
+transmitting requests to servers. Though its implementation is
|
|||
|
+simple, NFS over UDP has many limitations that prevent smooth
|
|||
|
+operation and good performance in some common deployment
|
|||
|
+environments. Even an insignificant packet loss rate results in the
|
|||
|
+loss of whole NFS requests; as such, retransmit timeouts are usually
|
|||
|
+in the subsecond range to allow clients to recover quickly from
|
|||
|
+dropped requests, but this can result in extraneous network traffic
|
|||
|
and server load.
|
|||
|
.P
|
|||
|
-However, UDP can be quite effective in specialized settings where
|
|||
|
-the network’s MTU is large relative to NFS’s data transfer size (such
|
|||
|
-as network environments that enable jumbo Ethernet frames). In such
|
|||
|
-environments, trimming the
|
|||
|
-.B rsize
|
|||
|
-and
|
|||
|
-.B wsize
|
|||
|
-settings so that each
|
|||
|
-NFS read or write request fits in just a few network frames (or even
|
|||
|
-in a single frame) is advised. This reduces the probability that
|
|||
|
-the loss of a single MTU-sized network frame results in the loss of
|
|||
|
+However, UDP can be quite effective in specialized settings where
|
|||
|
+the networks MTU is large relative to NFSs data transfer size (such
|
|||
|
+as network environments that enable jumbo Ethernet frames). In such
|
|||
|
+environments, trimming the
|
|||
|
+.B rsize
|
|||
|
+and
|
|||
|
+.B wsize
|
|||
|
+settings so that each
|
|||
|
+NFS read or write request fits in just a few network frames (or even
|
|||
|
+in a single frame) is advised. This reduces the probability that
|
|||
|
+the loss of a single MTU-sized network frame results in the loss of
|
|||
|
an entire large read or write request.
|
|||
|
.P
|
|||
|
-TCP is the default transport protocol used for all modern NFS
|
|||
|
+TCP is the default transport protocol used for all modern NFS
|
|||
|
implementations. It performs well in almost every conceivable
|
|||
|
-network environment and provides excellent guarantees against data
|
|||
|
-corruption caused by network unreliability. TCP is often a
|
|||
|
+network environment and provides excellent guarantees against data
|
|||
|
+corruption caused by network unreliability. TCP is often a
|
|||
|
requirement for mounting a server through a network firewall.
|
|||
|
.P
|
|||
|
Under normal circumstances, networks drop packets much more
|
|||
|
@@ -861,11 +910,11 @@ After the client exhausts its retransmits (the value of the
|
|||
|
.B retrans
|
|||
|
mount option), it assumes a network partition has occurred,
|
|||
|
and attempts to reconnect to the server on a fresh socket. Since
|
|||
|
-TCP itself makes network data transfer reliable,
|
|||
|
+TCP itself makes network data transfer reliable,
|
|||
|
.B rsize
|
|||
|
-and
|
|||
|
+and
|
|||
|
.B wsize
|
|||
|
-can safely be allowed to default to the largest values supported by
|
|||
|
+can safely be allowed to default to the largest values supported by
|
|||
|
both client and server, independent of the network's MTU size.
|
|||
|
.SS "Using the mountproto mount option"
|
|||
|
This section applies only to NFS version 2 and version 3 mounts
|
|||
|
@@ -950,8 +999,8 @@ Some modern cluster file systems provide
|
|||
|
perfect cache coherence among their clients.
|
|||
|
Perfect cache coherence among disparate NFS clients
|
|||
|
is expensive to achieve, especially on wide area networks.
|
|||
|
-As such, NFS settles for weaker cache coherence that
|
|||
|
-satisfies the requirements of most file sharing types. Normally,
|
|||
|
+As such, NFS settles for weaker cache coherence that
|
|||
|
+satisfies the requirements of most file sharing types. Normally,
|
|||
|
file sharing is completely sequential:
|
|||
|
first client A opens a file, writes something to it, then closes it;
|
|||
|
then client B opens the same file, and reads the changes.
|
|||
|
@@ -985,7 +1034,7 @@ it is still difficult to tell whether it was
|
|||
|
that client's updates or some other client's updates
|
|||
|
that altered the file.
|
|||
|
.SS "Attribute caching"
|
|||
|
-Use the
|
|||
|
+Use the
|
|||
|
.B noac
|
|||
|
mount option to achieve attribute cache coherence
|
|||
|
among multiple clients.
|
|||
|
@@ -1014,14 +1063,67 @@ The NFS protocol is not designed to support
|
|||
|
true cluster file system cache coherence
|
|||
|
without some type of application serialization.
|
|||
|
If absolute cache coherence among clients is required,
|
|||
|
-applications should use file locking. Alternatively, applications
|
|||
|
+applications should use file locking. Alternatively, applications
|
|||
|
can also open their files with the O_DIRECT flag
|
|||
|
to disable data caching entirely.
|
|||
|
+.SS "Directory entry caching"
|
|||
|
+The Linux NFS client caches the result of all NFS LOOKUP requests.
|
|||
|
+If the requested directory entry exists on the server,
|
|||
|
+the result is referred to as a
|
|||
|
+.IR positive " lookup result.
|
|||
|
+If the requested directory entry does not exist on the server
|
|||
|
+(that is, the server returned ENOENT),
|
|||
|
+the result is referred to as
|
|||
|
+.IR negative " lookup result.
|
|||
|
+.P
|
|||
|
+To detect when directory entries have been added or removed
|
|||
|
+on the server,
|
|||
|
+the Linux NFS client watches a directory's mtime.
|
|||
|
+If the client detects a change in a directory's mtime,
|
|||
|
+the client drops all cached LOOKUP results for that directory.
|
|||
|
+Since the directory's mtime is a cached attribute, it may
|
|||
|
+take some time before a client notices it has changed.
|
|||
|
+See the descriptions of the
|
|||
|
+.BR acdirmin ", " acdirmax ", and " noac
|
|||
|
+mount options for more information about
|
|||
|
+how long a directory's mtime is cached.
|
|||
|
+.P
|
|||
|
+Caching directory entries improves the performance of applications that
|
|||
|
+do not share files with applications on other clients.
|
|||
|
+Using cached information about directories can interfere
|
|||
|
+with applications that run concurrently on multiple clients and
|
|||
|
+need to detect the creation or removal of files quickly, however.
|
|||
|
+The
|
|||
|
+.B lookupcache
|
|||
|
+mount option allows some tuning of directory entry caching behavior.
|
|||
|
+.P
|
|||
|
+Before kernel release 2.6.28,
|
|||
|
+the Linux NFS client tracked only positive lookup results.
|
|||
|
+This permitted applications to detect new directory entries
|
|||
|
+created by other clients quickly while still providing some of the
|
|||
|
+performance benefits of caching.
|
|||
|
+If an application depends on the previous lookup caching behavior
|
|||
|
+of the Linux NFS client, you can use
|
|||
|
+.BR lookupcache=positive .
|
|||
|
+.P
|
|||
|
+If the client ignores its cache and validates every application
|
|||
|
+lookup request with the server,
|
|||
|
+that client can immediately detect when a new directory
|
|||
|
+entry has been either created or removed by another client.
|
|||
|
+You can specify this behavior using
|
|||
|
+.BR lookupcache=none .
|
|||
|
+The extra NFS requests needed if the client does not
|
|||
|
+cache directory entries can exact a performance penalty.
|
|||
|
+Disabling lookup caching
|
|||
|
+should result in less of a performance penalty than using
|
|||
|
+.BR noac ,
|
|||
|
+and has no effect on how the NFS client caches the attributes of files.
|
|||
|
+.P
|
|||
|
.SS "The sync mount option"
|
|||
|
The NFS client treats the
|
|||
|
.B sync
|
|||
|
mount option differently than some other file systems
|
|||
|
-(refer to
|
|||
|
+(refer to
|
|||
|
.BR mount (8)
|
|||
|
for a description of the generic
|
|||
|
.B sync
|
|||
|
@@ -1032,16 +1134,16 @@ If neither
|
|||
|
.B sync
|
|||
|
nor
|
|||
|
.B async
|
|||
|
-is specified (or if the
|
|||
|
-.B async
|
|||
|
+is specified (or if the
|
|||
|
+.B async
|
|||
|
option is specified),
|
|||
|
the NFS client delays sending application
|
|||
|
writes to the server
|
|||
|
-until any of these events occur:
|
|||
|
+until any of these events occur:
|
|||
|
.IP
|
|||
|
Memory pressure forces reclamation of system memory resources.
|
|||
|
.IP
|
|||
|
-An application flushes file data explicitly with
|
|||
|
+An application flushes file data explicitly with
|
|||
|
.BR sync (2),
|
|||
|
.BR msync (2),
|
|||
|
or
|
|||
|
@@ -1069,7 +1171,7 @@ but at a significant performance cost.
|
|||
|
Applications can use the O_SYNC open flag to force application
|
|||
|
writes to individual files to go to the server immediately without
|
|||
|
the use of the
|
|||
|
-.B sync
|
|||
|
+.B sync
|
|||
|
mount option.
|
|||
|
.SS "Using file locks with NFS"
|
|||
|
The Network Lock Manager protocol is a separate sideband protocol
|
|||
|
@@ -1104,15 +1206,15 @@ mount option. NLM locking must be disabled with the
|
|||
|
.B nolock
|
|||
|
option when using NFS to mount
|
|||
|
.I /var
|
|||
|
-because
|
|||
|
-.I /var
|
|||
|
+because
|
|||
|
+.I /var
|
|||
|
contains files used by the NLM implementation on Linux.
|
|||
|
.P
|
|||
|
Specifying the
|
|||
|
.B nolock
|
|||
|
option may also be advised to improve the performance
|
|||
|
of a proprietary application which runs on a single client
|
|||
|
-and uses file locks extensively.
|
|||
|
+and uses file locks extensively.
|
|||
|
.SS "NFS version 4 caching features"
|
|||
|
The data and metadata caching behavior of NFS version 4
|
|||
|
clients is similar to that of earlier versions.
|
|||
|
@@ -1196,7 +1298,7 @@ authentication, and in-transit data protection.
|
|||
|
The NFS version 4 specification mandates NFSv4 ACLs,
|
|||
|
RPCGSS authentication, and RPCGSS security flavors
|
|||
|
that provide per-RPC integrity checking and encryption.
|
|||
|
-Because NFS version 4 combines the
|
|||
|
+Because NFS version 4 combines the
|
|||
|
function of the sideband protocols into the main NFS protocol,
|
|||
|
the new security features apply to all NFS version 4 operations
|
|||
|
including mounting, file locking, and so on.
|
|||
|
@@ -1210,11 +1312,11 @@ that is in effect on a given NFS mount point.
|
|||
|
Specifying
|
|||
|
.B sec=krb5
|
|||
|
provides cryptographic proof of a user's identity in each RPC request.
|
|||
|
-This provides strong verification of the identity of users
|
|||
|
+This provides strong verification of the identity of users
|
|||
|
accessing data on the server.
|
|||
|
Note that additional configuration besides adding this mount option
|
|||
|
is required in order to enable Kerberos security.
|
|||
|
-Refer to the
|
|||
|
+Refer to the
|
|||
|
.BR rpc.gssd (8)
|
|||
|
man page for details.
|
|||
|
.P
|
|||
|
@@ -1299,15 +1401,15 @@ filter rules.
|
|||
|
It is still possible to mount an NFS server through a firewall,
|
|||
|
though some of the
|
|||
|
.BR mount (8)
|
|||
|
-command's automatic service endpoint discovery mechanisms may not work; this
|
|||
|
+command's automatic service endpoint discovery mechanisms may not work; this
|
|||
|
requires you to provide specific endpoint details via NFS mount options.
|
|||
|
.P
|
|||
|
NFS servers normally run a portmapper or rpcbind daemon to advertise
|
|||
|
-their service endpoints to clients. Clients use the rpcbind daemon to determine:
|
|||
|
+their service endpoints to clients. Clients use the rpcbind daemon to determine:
|
|||
|
.IP
|
|||
|
What network port each RPC-based service is using
|
|||
|
.IP
|
|||
|
-What transport protocols each RPC-based service supports
|
|||
|
+What transport protocols each RPC-based service supports
|
|||
|
.P
|
|||
|
The rpcbind daemon uses a well-known port number (111) to help clients find a service endpoint.
|
|||
|
Although NFS often uses a standard port number (2049),
|
|||
|
@@ -1339,9 +1441,9 @@ of the NFS version 3 specification, however.
|
|||
|
.P
|
|||
|
The NFS version 4 specification mandates a new version
|
|||
|
of Access Control Lists that are semantically richer than POSIX ACLs.
|
|||
|
-NFS version 4 ACLs are not fully compatible with POSIX ACLs; as such,
|
|||
|
+NFS version 4 ACLs are not fully compatible with POSIX ACLs; as such,
|
|||
|
some translation between the two is required
|
|||
|
-in an environment that mixes POSIX ACLs and NFS version 4.
|
|||
|
+in an environment that mixes POSIX ACLs and NFS version 4.
|
|||
|
.SH FILES
|
|||
|
.TP 1.5i
|
|||
|
.I /etc/fstab
|
|||
|
@@ -1418,4 +1520,4 @@ RFC 1833 for the RPC bind specification.
|
|||
|
.br
|
|||
|
RFC 2203 for the RPCSEC GSS API protocol specification.
|
|||
|
.br
|
|||
|
-RFC 3530 for the NFS version 4 specification.
|
|||
|
+RFC 3530 for the NFS version 4 specification.
|
|||
|
diff --git a/utils/mount/nfsmount.conf b/utils/mount/nfsmount.conf
|
|||
|
new file mode 100644
|
|||
|
index 0000000..f9fcfcb
|
|||
|
--- /dev/null
|
|||
|
+++ b/utils/mount/nfsmount.conf
|
|||
|
@@ -0,0 +1,120 @@
|
|||
|
+#
|
|||
|
+# /etc/nfsmount.conf - see nfsmount.conf(5) for details
|
|||
|
+#
|
|||
|
+# This is an NFS mount configuration file. This file can be broken
|
|||
|
+# up into three different sections: Mount, Server and Global
|
|||
|
+#
|
|||
|
+# [ MountPoint "Mount_point" ]
|
|||
|
+# This section defines all the mount options that
|
|||
|
+# should be used on a particular mount point. The '<Mount_Point>'
|
|||
|
+# string need to be an exact match of the path in the mount
|
|||
|
+# command. Example:
|
|||
|
+# [ MountPoint "/export/home" ]
|
|||
|
+# background=True
|
|||
|
+# Would cause all mount to /export/home would be done in
|
|||
|
+# the background
|
|||
|
+#
|
|||
|
+# [ Server "Server_Name" ]
|
|||
|
+# This section defines all the mount options that
|
|||
|
+# should be used on mounts to a particular NFS server.
|
|||
|
+# Example:
|
|||
|
+# [ Server "nfsserver.foo.com" ]
|
|||
|
+# rsize=32k
|
|||
|
+# wsize=32k
|
|||
|
+# All reads and writes to the 'nfsserver.foo.com' server
|
|||
|
+# will be done with 32k (32768 bytes) block sizes.
|
|||
|
+#
|
|||
|
+#[ NFSMount_Global_Options ]
|
|||
|
+# This statically named section defines global mount
|
|||
|
+# options that can be applied on all NFS mount.
|
|||
|
+#
|
|||
|
+# Protocol Version [2,3]
|
|||
|
+# Nfsvers=3
|
|||
|
+# Network Transport [Udp,Tcp,Rdma]
|
|||
|
+# Proto=Tcp
|
|||
|
+#
|
|||
|
+# The number of times a request will be retired before
|
|||
|
+# generating a timeout
|
|||
|
+# Retrans=2
|
|||
|
+#
|
|||
|
+# The number of minutes that will retry mount
|
|||
|
+# Retry=2
|
|||
|
+#
|
|||
|
+# The minimum time (in seconds) file attributes are cached
|
|||
|
+# acregmin=30
|
|||
|
+#
|
|||
|
+# The Maximum time (in seconds) file attributes are cached
|
|||
|
+# acregmin=60
|
|||
|
+#
|
|||
|
+# The minimum time (in seconds) directory attributes are cached
|
|||
|
+# acregmin=30
|
|||
|
+#
|
|||
|
+# The Maximum time (in seconds) directory attributes are cached
|
|||
|
+# acregmin=60
|
|||
|
+#
|
|||
|
+# Enable Access Control Lists
|
|||
|
+# Acl=False
|
|||
|
+#
|
|||
|
+# Enable Attribute Caching
|
|||
|
+# Ac=True
|
|||
|
+#
|
|||
|
+# Do mounts in background (i.e. asynchronously)
|
|||
|
+# Background=False
|
|||
|
+#
|
|||
|
+# Close-To-Open cache coherence
|
|||
|
+# Cto=True
|
|||
|
+#
|
|||
|
+# Do mounts in foreground (i.e. synchronously)
|
|||
|
+# Foreground=True
|
|||
|
+#
|
|||
|
+# How to handle times out from servers (Hard is STRONGLY suggested)
|
|||
|
+# Hard=True
|
|||
|
+# Soft=False
|
|||
|
+#
|
|||
|
+# Enable File Locking
|
|||
|
+# Lock=True
|
|||
|
+#
|
|||
|
+# Enable READDIRPLUS on NFS version 3 mounts
|
|||
|
+# Rdirplus=True
|
|||
|
+#
|
|||
|
+# Maximum Read Size (in Bytes)
|
|||
|
+# Rsize=8k
|
|||
|
+#
|
|||
|
+# Maximum Write Size (in Bytes)
|
|||
|
+# Wsize=8k
|
|||
|
+#
|
|||
|
+# Maximum Server Block Size (in Bytes)
|
|||
|
+# Bsize=8k
|
|||
|
+#
|
|||
|
+# Ignore unknown mount options
|
|||
|
+# Sloppy=False
|
|||
|
+#
|
|||
|
+# Share Data and Attribute Caches
|
|||
|
+# Sharecache=True
|
|||
|
+#
|
|||
|
+# The amount of time, in tenths of a seconds, the client
|
|||
|
+# will wait for a response from the server before retransmitting
|
|||
|
+# the request.
|
|||
|
+# Timeo=600
|
|||
|
+#
|
|||
|
+# Sets all attributes times to the same time (in seconds)
|
|||
|
+# actimeo=30
|
|||
|
+#
|
|||
|
+# Server Mountd port mountport
|
|||
|
+# mountport=4001
|
|||
|
+#
|
|||
|
+# Server Mountd Protocol
|
|||
|
+# mountproto=tcp
|
|||
|
+#
|
|||
|
+# Server Mountd Version
|
|||
|
+# mounvers=3
|
|||
|
+#
|
|||
|
+# Server Mountd Host
|
|||
|
+# mounthost=hostname
|
|||
|
+#
|
|||
|
+# Server Port
|
|||
|
+# Port=2049
|
|||
|
+#
|
|||
|
+# RPCGSS security flavors
|
|||
|
+# [none, sys, krb5, krb5i, krb5p ]
|
|||
|
+# Sec=sys
|
|||
|
diff --git a/utils/mount/nfsmount.conf.man b/utils/mount/nfsmount.conf.man
|
|||
|
new file mode 100644
|
|||
|
index 0000000..12a3fe7
|
|||
|
--- /dev/null
|
|||
|
+++ b/utils/mount/nfsmount.conf.man
|
|||
|
@@ -0,0 +1,87 @@
|
|||
|
+.\"@(#)nfsmount.conf.5"
|
|||
|
+.TH NFSMOUNT.CONF 5 "9 Mar 2008"
|
|||
|
+.SH NAME
|
|||
|
+nfsmount.conf - Configuration file for NFS mounts
|
|||
|
+.SH SYNOPSIS
|
|||
|
+Configuration file for NFS mounts that allows options
|
|||
|
+to be set globally, per server or per mount point.
|
|||
|
+.SH DESCRIPTION
|
|||
|
+The configuration file is made up of multiple sections
|
|||
|
+followed by variables associated with that section.
|
|||
|
+A section is defined by a string enclosed by
|
|||
|
+.BR [
|
|||
|
+and
|
|||
|
+.BR ]
|
|||
|
+branches.
|
|||
|
+Variables are assignment statements that assign values
|
|||
|
+to particular variables using the
|
|||
|
+.BR =
|
|||
|
+operator, as in
|
|||
|
+.BR Proto=Tcp .
|
|||
|
+Sections are broken up into three basic categories:
|
|||
|
+Global options, Server options and Mount Point options.
|
|||
|
+.HP
|
|||
|
+.B [ NFSMount_Global_Options ]
|
|||
|
+- This statically named section
|
|||
|
+defines all of the global mount options that can be
|
|||
|
+applied to every NFS mount.
|
|||
|
+.HP
|
|||
|
+.B [ Server \(lqServer_Name\(rq ]
|
|||
|
+- This section defines all the mount options that should
|
|||
|
+be used on mounts to a particular NFS server. The
|
|||
|
+.I \(lqServer_Name\(rq
|
|||
|
+strings needs to be surrounded by '\(lq' and
|
|||
|
+be an exact match of the server name used in the
|
|||
|
+.B mount
|
|||
|
+command.
|
|||
|
+.HP
|
|||
|
+.B [ MountPoint \(lqMount_Point\(rq ]
|
|||
|
+- This section defines all the mount options that
|
|||
|
+should be used on a particular mount point.
|
|||
|
+The
|
|||
|
+.I \(lqMount_Point\(rq
|
|||
|
+string needs to be surrounded by '\(lq' and be an
|
|||
|
+exact match of the mount point used in the
|
|||
|
+.BR mount
|
|||
|
+command.
|
|||
|
+.SH EXAMPLES
|
|||
|
+.PP
|
|||
|
+These are some example lines of how sections and variables
|
|||
|
+are defined in the configuration file.
|
|||
|
+.PP
|
|||
|
+[ NFSMount_Global_Options ]
|
|||
|
+.br
|
|||
|
+ Proto=Tcp
|
|||
|
+.RS
|
|||
|
+.HP
|
|||
|
+The TCP protocol will be used on every NFS mount.
|
|||
|
+.HP
|
|||
|
+.RE
|
|||
|
+[ Server \(lqnfsserver.foo.com\(rq ]
|
|||
|
+.br
|
|||
|
+ rsize=32k
|
|||
|
+.br
|
|||
|
+ wsize=32k
|
|||
|
+.HP
|
|||
|
+.RS
|
|||
|
+A 33k (32768 bytes) block size will be used as the read and write
|
|||
|
+size on all mounts to the 'nfsserver.foo.com' server.
|
|||
|
+.HP
|
|||
|
+.RE
|
|||
|
+.BR
|
|||
|
+[ MountPoint \(lq/export/home\(rq ]
|
|||
|
+.br
|
|||
|
+ Background=True
|
|||
|
+.RS
|
|||
|
+.HP
|
|||
|
+All mounts to the '/export/home' export will be performed in
|
|||
|
+the background (i.e. done asynchronously).
|
|||
|
+.HP
|
|||
|
+.SH FILES
|
|||
|
+.TP 10n
|
|||
|
+.I /etc/nfsmount.conf
|
|||
|
+Default NFS mount configuration file
|
|||
|
+.PD
|
|||
|
+.SH SEE ALSO
|
|||
|
+.BR nfs (5),
|
|||
|
+.BR mount (8),
|
|||
|
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
|
|||
|
index 9bbbfb3..e4e2f22 100644
|
|||
|
--- a/utils/mountd/cache.c
|
|||
|
+++ b/utils/mountd/cache.c
|
|||
|
@@ -564,7 +564,7 @@ static void write_fsloc(FILE *f, struct exportent *ep, char *path)
|
|||
|
release_replicas(servers);
|
|||
|
}
|
|||
|
|
|||
|
-static void write_secinfo(FILE *f, struct exportent *ep)
|
|||
|
+static void write_secinfo(FILE *f, struct exportent *ep, int flag_mask)
|
|||
|
{
|
|||
|
struct sec_entry *p;
|
|||
|
|
|||
|
@@ -578,7 +578,7 @@ static void write_secinfo(FILE *f, struct exportent *ep)
|
|||
|
qword_printint(f, p - ep->e_secinfo);
|
|||
|
for (p = ep->e_secinfo; p->flav; p++) {
|
|||
|
qword_printint(f, p->flav->fnum);
|
|||
|
- qword_printint(f, p->flags);
|
|||
|
+ qword_printint(f, p->flags & flag_mask);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
@@ -590,16 +590,14 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex
|
|||
|
qword_printint(f, time(0)+30*60);
|
|||
|
if (exp) {
|
|||
|
int different_fs = strcmp(path, exp->e_path) != 0;
|
|||
|
-
|
|||
|
- if (different_fs)
|
|||
|
- qword_printint(f, exp->e_flags & ~NFSEXP_FSID);
|
|||
|
- else
|
|||
|
- qword_printint(f, exp->e_flags);
|
|||
|
+ int flag_mask = different_fs ? ~NFSEXP_FSID : ~0;
|
|||
|
+
|
|||
|
+ qword_printint(f, exp->e_flags & flag_mask);
|
|||
|
qword_printint(f, exp->e_anonuid);
|
|||
|
qword_printint(f, exp->e_anongid);
|
|||
|
qword_printint(f, exp->e_fsid);
|
|||
|
write_fsloc(f, exp, path);
|
|||
|
- write_secinfo(f, exp);
|
|||
|
+ write_secinfo(f, exp, flag_mask);
|
|||
|
if (exp->e_uuid == NULL || different_fs) {
|
|||
|
char u[16];
|
|||
|
if (get_uuid(path, NULL, 16, u)) {
|
|||
|
diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
|
|||
|
index 7e9f327..fa46d5d 100644
|
|||
|
--- a/utils/nfsstat/nfsstat.c
|
|||
|
+++ b/utils/nfsstat/nfsstat.c
|
|||
|
@@ -30,8 +30,8 @@ static unsigned int cltproc2info[20], cltproc2info_old[20]; /* NFSv2 call counts
|
|||
|
static unsigned int srvproc3info[24], srvproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */
|
|||
|
static unsigned int cltproc3info[24], cltproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */
|
|||
|
static unsigned int srvproc4info[4], srvproc4info_old[4]; /* NFSv4 call counts ([0] == 2) */
|
|||
|
-static unsigned int cltproc4info[37], cltproc4info_old[37]; /* NFSv4 call counts ([0] == 35) */
|
|||
|
-static unsigned int srvproc4opsinfo[42], srvproc4opsinfo_old[42]; /* NFSv4 call counts ([0] == 40) */
|
|||
|
+static unsigned int cltproc4info[49], cltproc4info_old[49]; /* NFSv4 call counts ([0] == 35) */
|
|||
|
+static unsigned int srvproc4opsinfo[61], srvproc4opsinfo_old[61]; /* NFSv4 call counts ([0] == 40) */
|
|||
|
static unsigned int srvnetinfo[5], srvnetinfo_old[5]; /* 0 # of received packets
|
|||
|
* 1 UDP packets
|
|||
|
* 2 TCP packets
|
|||
|
@@ -93,24 +93,58 @@ static const char * nfssrvproc4name[2] = {
|
|||
|
"compound",
|
|||
|
};
|
|||
|
|
|||
|
-static const char * nfscltproc4name[35] = {
|
|||
|
+static const char * nfscltproc4name[47] = {
|
|||
|
"null", "read", "write", "commit", "open", "open_conf",
|
|||
|
"open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew",
|
|||
|
"setclntid", "confirm", "lock",
|
|||
|
"lockt", "locku", "access", "getattr", "lookup", "lookup_root",
|
|||
|
"remove", "rename", "link", "symlink", "create", "pathconf",
|
|||
|
"statfs", "readlink", "readdir", "server_caps", "delegreturn", "getacl",
|
|||
|
- "setacl", "fs_locations"
|
|||
|
+ "setacl", "fs_locations",
|
|||
|
+ /* nfsv4.1 client ops */
|
|||
|
+ "exchange_id",
|
|||
|
+ "create_ses",
|
|||
|
+ "destroy_ses",
|
|||
|
+ "sequence",
|
|||
|
+ "get_lease_t",
|
|||
|
+ "layoutget",
|
|||
|
+ "layoutcommit",
|
|||
|
+ "layoutreturn",
|
|||
|
+ "getdevlist",
|
|||
|
+ "getdevinfo",
|
|||
|
+ /* nfsv4.1 pnfs client ops to data server only */
|
|||
|
+ "ds_write",
|
|||
|
+ "ds_commit",
|
|||
|
};
|
|||
|
|
|||
|
-static const char * nfssrvproc4opname[40] = {
|
|||
|
+static const char * nfssrvproc4opname[59] = {
|
|||
|
"op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
|
|||
|
"create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
|
|||
|
"lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
|
|||
|
"open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
|
|||
|
"putrootfh", "read", "readdir", "readlink", "remove", "rename",
|
|||
|
"renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
|
|||
|
- "setcltidconf", "verify", "write", "rellockowner"
|
|||
|
+ "setcltidconf", "verify", "write", "rellockowner",
|
|||
|
+ /* nfsv4.1 server ops */
|
|||
|
+ "bc_ctl",
|
|||
|
+ "bind_conn",
|
|||
|
+ "exchange_id",
|
|||
|
+ "create_ses",
|
|||
|
+ "destroy_ses",
|
|||
|
+ "free_stateid",
|
|||
|
+ "getdirdeleg",
|
|||
|
+ "getdevinfo",
|
|||
|
+ "getdevlist",
|
|||
|
+ "layoutcommit",
|
|||
|
+ "layoutget",
|
|||
|
+ "layoutreturn",
|
|||
|
+ "secinfononam",
|
|||
|
+ "sequence",
|
|||
|
+ "set_ssv",
|
|||
|
+ "test_stateid",
|
|||
|
+ "want_deleg",
|
|||
|
+ "destroy_clid",
|
|||
|
+ "reclaim_comp",
|
|||
|
};
|
|||
|
|
|||
|
#define LABEL_srvnet "Server packet stats:\n"
|