Patchwork [09/12] nsdbparams: Use positional parameters for mandatory command line options

login
register
mail settings
Submitter Chuck Lever
Date Nov. 11, 2011, 8:27 p.m.
Message ID <20111111202717.10717.16847.stgit@degas.1015granger.net>
Download mbox | patch
Permalink /patch/125290/
State Accepted
Headers show

Comments

Chuck Lever - Nov. 11, 2011, 8:27 p.m.
We're trying to keep the Solaris and Linux administrative interfaces
roughly the same, to make it easy for admins to use either one without
a broad learning curve.

Rob Thurlow mentions that Solaris user interface guidelines require
that mandatory command line options must be specified as positional
parameters rather than by using dash switches.  The only reason I used
dash switches for all the options was sheer laziness.

To make this change, I'm going to separate the subcommands into their
own source files, and clone the option parsing logic into each one.
That means that each subcommand can now check for specific options,
and flag unrecognized options that other subcommands might recognize.
And, each subcommand now has its own usage message.

For the "show," "update," and "delete" subcommands, make the NSDB
hostname option a positional parameter.

This also involves a rather extensive rewrite of the nsdbparams(8) man
page.

Overall, this change should not only improve the usability of the
nsdbparams command, but it will allow us to add new nsdbparams
subcommands quickly.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 doc/man/nsdbparams.8        |  220 +++++++++++++-------
 src/nsdbparams/Makefile.am  |    5 
 src/nsdbparams/delete.c     |  234 +++++++++++++++++++++
 src/nsdbparams/list.c       |  206 ++++++++++++++++++
 src/nsdbparams/main.c       |  485 ++-----------------------------------------
 src/nsdbparams/nsdbparams.h |   35 +++
 src/nsdbparams/show.c       |  241 +++++++++++++++++++++
 src/nsdbparams/update.c     |  371 +++++++++++++++++++++++++++++++++
 8 files changed, 1260 insertions(+), 537 deletions(-)
 create mode 100644 src/nsdbparams/delete.c
 create mode 100644 src/nsdbparams/list.c
 create mode 100644 src/nsdbparams/nsdbparams.h
 create mode 100644 src/nsdbparams/show.c
 create mode 100644 src/nsdbparams/update.c

Patch

diff --git a/doc/man/nsdbparams.8 b/doc/man/nsdbparams.8
index 3f8ed8d..3ad5f01 100644
--- a/doc/man/nsdbparams.8
+++ b/doc/man/nsdbparams.8
@@ -27,9 +27,50 @@ 
 .SH NAME
 nsdbparams \- manage local NSDB connection parameter database
 .SH SYNOPSIS
-.B nsdbparams
-.IR subcommand " ["
-.IR options " ]"
+.B nsdbparams delete
+.RB [ \-?d ]
+.RB [ \-g
+.IR gid ]
+.RB [ \-r
+.IR nsdbport ]
+.RB [ \-u
+.IR uid ]
+.I nsdbname
+.P
+.B nsdbparams list
+.RB [ \-?d ]
+.RB [ \-u
+.IR uid ]
+.RB [ \-g
+.IR gid ]
+.P
+.B nsdbparams show
+.RB [ \-?d ]
+.RB [ \-g
+.IR gid ]
+.RB [ \-r
+.IR nsdbport ]
+.RB [ \-u
+.IR uid ]
+.I nsdbname
+.P
+.B nsdbparams update
+.RB [ \-?dR ]
+.RB [ \-D
+.IR def-binddn ]
+.RB [ \-e
+.IR def-nce ]
+.RB [ \-f
+.IR certfile ]
+.RB [ \-g
+.IR gid ]
+.RB [ \-r
+.IR nsdbport ]
+.RB [ \-t
+.IR sectype ]
+.RB [ \-u
+.IR uid ]
+.I nsdbname
 .SH INTRODUCTION
 RFC 5716 introduces the Federated File System (FedFS, for short).
 FedFS is an extensible standardized mechanism
@@ -44,10 +85,11 @@  on one or more LDAP servers.
 These servers are known as
 .IR "namespace databases" ,
 or NSDBs, for short.
-A local NSDB connection parameter database is used
-to remember connection parameters for each NSDB
-during FedFS server-side operations
-and when executing FedFS administrative commands.
+On systems that contact NSDBs,
+a local database stores connection parameters for each NSDB.
+These connection parameters are used during FedFS server-side operations
+that access NSDBs,
+and when executing NSDB administrative commands.
 .SH DESCRIPTION
 The
 .BR nsdbparams (8)
@@ -58,35 +100,44 @@  such as the preferred bind DN and the location of the
 NSDB container entry,
 for each NSDB the local system knows about.
 .P
-Note that some NSDB connection parameters
-are also remotely accessible via
+Some NSDB connection parameters are also remotely accessible via
 .BR rpc.fedfsd (8).
 The
 .BR nsdbparams (8)
-command allows complete access to the local system's NSDB database,
+command allows complete access to the local system's NSDB database
 including access to some parameters which are not accessible to clients of
 .BR rpc.fedfsd (8).
 .P
+Typically
+.BR rpc.fedfsd (8)
+runs only on FedFS-enabled file servers.
 FedFS administrators can manage NSDB connection parameters with
 .BR nsdbparams (8)
 on a system that is not running
-.BR rpc.fedfsd (8)
-(say, on a system that is acting as a FedFS administrative client,
-but not as a FedFS-enabled file server).
+.BR rpc.fedfsd (8),
+such as a system that is acting only as a FedFS administrative client.
+Connection parameters for NSDBs must be stored
+in the local NSDB connection parameter database
+before FedFS junction resolution and
+NSDB administrative commands can work.
 .SS Operation
-The NSDB connection parameter database is typically stored
-in a directory that is owned by a special UID and GID.
+The NSDB connection parameter database is stored
+in a directory
+(typically
+.IR @statedir )
+that is owned by a special UID and GID.
 Therefore, this command must be run as root.
 During operation,
 .BR nsdbparams (8)
 drops its root privileges,
 running as the special user and group instead.
-The default value of the special IDs are determined when
+.P
+The default value of these special IDs is determined when
 .BR nsdbparams (8)
-is built, but can also be specified at run time using the
-.B --uid
+is built.  They can also be specified at run time using the
+.B \-\-uid
 or
-.B --gid
+.B \-\-gid
 command line options.
 .P
 When executing a subcommand,
@@ -95,128 +146,143 @@  verifies that the local NSDB connection parameter database exists
 and is accessible.
 If it does not exist,
 .BR nsdbparams (8)
-attempts to create the connection parameter database.
+attempts to create and initialize a new connection parameter database.
 If it cannot, the subcommand fails.
 .SS Subcommands
-Valid subcommands are:
+Valid
+.BR nsdbparams (8)
+subcommands are:
 .IP "\fBdelete\fP"
-Remove the entry for the specified NSDB.
+Remove the connection parameters for the specified NSDB
+from the local NSDB connection parameter database.
 If this subcommand succeeds,
-subsequent access of the specified NSDB on the local system fails.
+subsequent attempts to access the specified NSDB on the local system fail.
 .IP "\fBlist\fP"
-Display a list of all entries in the local NSDB connection parameter database.
-An abbreviated form of the parameters are shown for each NSDB.
+Display a list of all NSDBs in the local NSDB connection parameter database.
+An abbreviated form of the connection parameters for each known NSDB
+are shown.
+This subcommand does not take an NSDB domain name parameter.
 .IP "\fBupdate\fP"
-Update the entry for the specified NSDB.
+Update the connection parameters for the specified NSDB
+in the local NSDB connection parameter database.
 Use this subcommand to
 add a new entry for an NSDB to the local connection parameter database,
 or to modify an existing entry in the database.
 .IP "\fBshow\fP"
-Display the entry for the specified NSDB.
-This subcommand displays all known local settings for the specified NSDB.
+Display the recorded connection parameters for the specified NSDB.
+This subcommand displays all known settings for the specified NSDB
+stored in the local NSDB connection parameter database.
 .P
-The NSDB domain name and the port number (see below)
-are used as the primary key to identify an entry in the NSDB
+The NSDB domain name and IP port number pair
+are used as the primary key to identify an NSDB to the NSDB
 connection parameter database.
 The subcommands
 .BR delete ,
 .BR update ", and"
 .B show
-require that the
-.I -l
-option be specified.
-The NSDB port number assumes a default value, and thus is optional.
+require that an NSDB domain name be specified as a positional parameter.
+If no NSDB port number is provided on the command line, the
+.BR nsdbparams (8)
+command uses the default LDAP port (389).
 .P
 The database matches NSDB domain names and ports by exact value.
-In other words,
-if two unique domain names point
+If two unique domain names point
 to the IP address of the same physical NSDB,
-they are still considered separate entries
+they are considered separate entries
 in the local NSDB connection parameter database.
 .SS Command line options
 .IP "\fB\-d, \-\-debug"
-Specifies that debugging messages be produced during operation.
+Enables debugging messages during subcommand operation.
+This option is valid for all subcommands.
 .IP "\fB\-D, \-\-binddn=\fIbind-DN\fP"
 Specifies the default LDAP distinguished name to use
-when binding to the specified NSDB during administrative operations.
+when binding to the specified NSDB for administrative operations.
+This option is valid for the
+.B update
+subcommand.
 .IP "\fB-e, \-\-nce=\fINCE-DN\fP"
 Specifies the default LDAP distinguished name of the NSDB container entry
-for the specified NSDB during administrative operations.
+for the specified NSDB for administrative operations.
+This option is valid for the
+.B update
+subcommand.
 .IP "\fB-f, \-\-certfile=\fIpathname\fP"
 Specifies the pathname of a local file containing the X.509 certificate
-to use for establishing a TLS connection with the specified NSDB.
+to establish a TLS connection with the specified NSDB.
 The
 .BR nsdbparams (8)
-command copies the certificate to a private directory.
-The specified file may be deleted if the subcommand succeeds.
+command copies the specified certificate to a private directory.
+The specified file can be deleted after the subcommand succeeds.
+This option is valid for the
+.B update
+subcommand.
 .IP "\fB\-g, \-\-gid=\fIid\fP"
-Specifies the numeric or text GID that
-.BR rpc.fedfsd (8)
-runs under after dropping root privileges.
+Specifies the numeric or text GID that the
+.BR nsdbparams (8)
+command runs as after dropping root privileges.
 By default, the GID for the group
 .I @fedfsuser@
 is used.
 If that group doesn't exist, then the GID for
 .I nobody
 is used instead.
+This option is valid for all subcommands.
 .IP "\fB\-?, \-\-help"
-Prints
+Displays
 .BR nsdbparams (8)
-version and usage message on
-.IR stderr ,
-then exits.
-.IP "\fB\-l, \-\-nsdbname=\fINSDB-domain-name\fP"
-Specifies the domain name of the NSDB.
-This and the port number are used as the primary key for matching
-NSDB connection parameters in this database.
+version information and a subcommand usage message on
+.IR stderr .
+This option is valid for all subcommands.
 .IP "\fB\-r, \-\-nsdbport=\fINSDB-port\fP"
 Specifies the IP port for the specified NSDB.
-This and the NSDB domain name are used as the primary key for matching
-NSDB connection parameters in this database.
 The default value if this option is not specified is 389.
+This option is valid for any subcommand that requires an
+NSDB domain name to be specified.
 .IP "\fB\-R, \-\-referral=\fP[\fByes\fP|\fBno\fP]"
 Specifies whether or not the local system should follow LDAP referrals
 received from the specified NSDB.
+This option is valid for the
+.B update
+subcommand.
 .IP "\fB\-t, \-\-sectype=\fIsecurity-type\fP"
 Specifies the FedFS connection security type to use when connecting
 to the specified NSDB.  Valid values for
 .I security-type
 are
 .BR 0 ,
+.BR none ,
 .BR FEDFS_SEC_NONE ,
 .BR 1 ,
+.BR tls ,
 or
 .BR FEDFS_SEC_TLS .
+This option is valid for the
+.B update
+subcommand.
 .IP "\fB\-u, \-\-uid=\fIid\fP"
 Specifies the numeric or text UID that
-.BR rpc.fedfsd (8)
-runs under after dropping root privileges.
+.BR nsdbparams (8)
+runs as after dropping root privileges.
 By default, the UID for the user
 .I @fedfsuser@
 is used.
 If that user doesn't exist, then the UID for
 .I nobody
 is used instead.
+This option is valid for all subcommands.
 .SH EXAMPLES
-On systems that act only as FedFS administrative clients,
-connection parameters for NSDBs must be stored
-in the local NSDB connection parameter database
-before FedFS administrative commands can work.
-.P
-For example, if there is an NSDB in the
-.I example.net
-domain called
+If there is an NSDB called
 .IR nsdb.example.net ,
 the first command you might issue on a new administrative client might be:
 .RS
 .sp
-sudo nsdbparams update --nsdbname=nsdb.example.net
+# nsdbparams update nsdb.example.net
 .sp
 .RE
-You can view the new entry with
+You can view the new connection parameter entry with
 .RS
 .sp
-sudo nsdbparams show --nsdbname=nsdb.example.net
+# nsdbparams show nsdb.example.net
 .sp
 .RE
 The result of this command would look like:
@@ -232,15 +298,25 @@  nsdb.example.net:389:
 To set up TLS security, use the
 .B update
 subcommand and specify the
-.I --sectype
+.B \-\-sectype
 and
-.I --certfile
+.B \-\-certfile
 options.
+For instance, if an X.509 certificate for
+.I nsdb.example.net
+were contained in a local file called
+.IR /tmp/nsdb.pem ,
+you might use:
+.RS
+.sp
+# nsdbparams update -t tls -f /tmp/nsdb.pem nsdb.example.net
+.sp
+.RE
 To switch from TLS security back to no connection security for this NSDB,
 you might use:
 .RS
 .sp
-sudo nsdbparams update --nsdbparams=nsdb.example.net --sectype FEDFS_SEC_NONE
+# nsdbparams update nsdb.example.net -t none
 .SH FILES
 .TP
 .I @statedir@/nsdbparam.sqlite3
diff --git a/src/nsdbparams/Makefile.am b/src/nsdbparams/Makefile.am
index a9a0fa1..ddc3cd9 100644
--- a/src/nsdbparams/Makefile.am
+++ b/src/nsdbparams/Makefile.am
@@ -4,7 +4,7 @@ 
 ##
 
 ##
-## Copyright 2010 Oracle.  All rights reserved.
+## Copyright 2010, 2011 Oracle.  All rights reserved.
 ##
 ## This file is part of fedfs-utils.
 ##
@@ -23,8 +23,9 @@ 
 ##	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 ##
 
+noinst_HEADERS		= nsdbparams.h
 sbin_PROGRAMS		= nsdbparams
-nsdbparams_SOURCES	= main.c
+nsdbparams_SOURCES	= delete.c list.c main.c show.c update.c
 LDADD			= $(LIBLDAP) $(LIBLBER) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) \
 			  $(top_builddir)/src/libnsdb/libnsdb.a \
diff --git a/src/nsdbparams/delete.c b/src/nsdbparams/delete.c
new file mode 100644
index 0000000..37f30b1
--- /dev/null
+++ b/src/nsdbparams/delete.c
@@ -0,0 +1,234 @@ 
+/**
+ * @file src/nsdbparams/delete.c
+ * @brief Delete an item in the local NSDB connection parameters database
+ */
+
+/*
+ * Copyright 2010, 2011 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "fedfs.h"
+#include "nsdb.h"
+#include "xlog.h"
+#include "gpl-boiler.h"
+#include "nsdbparams.h"
+
+/**
+ * Short form command line options
+ */
+static const char nsdbparams_delete_opts[] = "?dg:r:u:";
+
+/**
+ * Long form command line options
+ */
+static const struct option nsdbparams_delete_longopts[] = {
+	{ "debug", 0, NULL, 'd', },
+	{ "gid", 1, NULL, 'g', },
+	{ "help", 0, NULL, '?', },
+	{ "nsdbport", 1, NULL, 'r', },
+	{ "uid", 1, NULL, 'u', },
+	{ NULL, 0, NULL, 0, },
+};
+
+/**
+ * Display subcommand synopsis
+ *
+ * @param progname NUL-terminate C string containing name of program
+ */
+static void
+nsdbparams_delete_usage(const char *progname)
+{
+	fprintf(stderr, "\nUsage: %s delete [options] NSDBNAME\n\n", progname);
+
+	fprintf(stderr, "\t-?, --help           Print this help\n");
+	fprintf(stderr, "\t-d, --debug          Enable debug messages\n");
+	fprintf(stderr, "\t-g, --gid            Run as this effective gid\n");
+	fprintf(stderr, "\t-r, --nsdbport       NSDB port\n");
+	fprintf(stderr, "\t-u, --uid            Run as this effective uid\n");
+
+	fprintf(stderr, "%s", fedfs_gpl_boilerplate);
+}
+
+/**
+ * Delete an NSDB entry in our NSDB connection parameter database
+ *
+ * @param progname NUL-terminated UTF-8 string containing name of this program
+ * @param argc count of command line arguments
+ * @param argv array of NUL-terminated C strings containing command line arguments
+ * @return program exit status
+ */
+int
+nsdbparams_delete(const char *progname, int argc, char **argv)
+{
+	unsigned short nsdbport = LDAP_PORT;
+	char *nsdbname, *endptr;
+	FedFsStatus status;
+	unsigned long tmp;
+	struct passwd *pw;
+	struct group *grp;
+	nsdb_t host;
+	uid_t uid;
+	gid_t gid;
+	int arg;
+
+	/* Discover the user ID who owns the store */
+	uid = 99;
+	gid = 99;
+	pw = getpwnam(FEDFS_USER);
+	if (pw != NULL) {
+		uid = pw->pw_uid;
+		gid = pw->pw_gid;
+		xlog(D_GENERAL, "Found user %s: UID %u and GID %u",
+			FEDFS_USER, uid, gid);
+	}
+
+	/* so that getopt_long(3)'s error messages are meaningful */
+	while ((arg = getopt_long(argc, argv, nsdbparams_delete_opts,
+				nsdbparams_delete_longopts, NULL)) != -1) {
+		switch (arg) {
+		case 'd':
+			xlog_config(D_ALL, 1);
+			xlog_stderr(1);
+			break;
+		case 'g':
+			if (optarg == NULL || *optarg == '\0') {
+				xlog(L_ERROR, "Invalid gid specified");
+				nsdbparams_delete_usage(progname);
+				return EXIT_FAILURE;
+			}
+
+			errno = 0;
+			tmp = strtoul(optarg, &endptr, 10);
+			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
+				grp = getgrnam(optarg);
+				if (grp == NULL) {
+					xlog(L_ERROR, "Invalid gid specified");
+					return EXIT_FAILURE;
+				}
+			} else {
+				grp = getgrgid((gid_t)tmp);
+				if (grp == NULL) {
+					xlog(L_ERROR, "Invalid gid specified");
+					return EXIT_FAILURE;
+				}
+			}
+			gid = grp->gr_gid;
+			break;
+		case 'h':
+		case '?':
+			nsdbparams_delete_usage(progname);
+			return EXIT_FAILURE;
+		case 'r':
+			if (!nsdb_parse_port_string(optarg, &nsdbport)) {
+				xlog(L_ERROR, "Bad port number: %s",
+					optarg);
+				nsdbparams_delete_usage(progname);
+				return EXIT_FAILURE;
+			}
+			break;
+		case 'u':
+			if (optarg == NULL || *optarg == '\0') {
+				xlog(L_ERROR, "Invalid uid specified");
+				nsdbparams_delete_usage(progname);
+				return EXIT_FAILURE;
+			}
+
+			errno = 0;
+			tmp = strtoul(optarg, &endptr, 10);
+			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
+				pw = getpwnam(optarg);
+				if (pw == NULL) {
+					xlog(L_ERROR, "Invalid uid specified");
+					return EXIT_FAILURE;
+				}
+			} else {
+				pw = getpwuid((uid_t)tmp);
+				if (pw == NULL) {
+					xlog(L_ERROR, "Invalid uid specified");
+					return EXIT_FAILURE;
+				}
+			}
+			uid = pw->pw_uid;
+			gid = pw->pw_gid;
+			break;
+		default:
+			xlog(L_ERROR, "Invalid command line "
+				"argument: %c", (char)arg);
+			nsdbparams_delete_usage(progname);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (argc == optind + 1)
+		nsdbname = argv[optind];
+	else if (argc > optind + 1) {
+		xlog(L_ERROR, "Unrecognized positional parameters");
+		nsdbparams_delete_usage(progname);
+		return EXIT_FAILURE;
+	} else {
+		xlog(L_ERROR, "No NSDB hostname was specified");
+		nsdbparams_delete_usage(progname);
+		return EXIT_FAILURE;
+	}
+	nsdbname = argv[optind];
+
+	if (!nsdb_create_basedir())
+		return EXIT_FAILURE;
+
+	if (!nsdbparams_drop_privileges(uid, gid))
+		return EXIT_FAILURE;
+
+	if (!nsdb_init_database())
+		return EXIT_FAILURE;
+
+	status = nsdb_lookup_nsdb(nsdbname, nsdbport, &host, NULL);
+	switch (status) {
+	case FEDFS_OK:
+		nsdb_free_nsdb(host);
+		status = nsdb_delete_nsdb(nsdbname, nsdbport);
+		if (status != FEDFS_OK) {
+			xlog(L_ERROR, "nsdb_delete_nsdb returned %s",
+				nsdb_display_fedfsstatus(status));
+			return EXIT_FAILURE;
+		}
+		printf("%s:%u was deleted successfully\n", nsdbname, nsdbport);
+		break;
+	case FEDFS_ERR_NSDB_PARAMS:
+		xlog(L_ERROR, "No record for %s:%u was found",
+			nsdbname, nsdbport);
+		break;
+	default:
+		xlog(L_ERROR, "nsdb_lookup_nsdb returned %s",
+			nsdb_display_fedfsstatus(status));
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/src/nsdbparams/list.c b/src/nsdbparams/list.c
new file mode 100644
index 0000000..e79b896
--- /dev/null
+++ b/src/nsdbparams/list.c
@@ -0,0 +1,206 @@ 
+/**
+ * @file src/nsdbparams/list.c
+ * @brief List entries in local NSDB connection parameters database
+ */
+
+/*
+ * Copyright 2011 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "fedfs.h"
+#include "nsdb.h"
+#include "xlog.h"
+#include "gpl-boiler.h"
+#include "nsdbparams.h"
+
+/**
+ * Short form command line options
+ */
+static const char nsdbparams_list_opts[] = "?dg:u:";
+
+/**
+ * Long form command line options
+ */
+static const struct option nsdbparams_list_longopts[] = {
+	{ "debug", 0, NULL, 'd', },
+	{ "gid", 1, NULL, 'g', },
+	{ "help", 0, NULL, '?', },
+	{ "uid", 1, NULL, 'u', },
+	{ NULL, 0, NULL, 0, },
+};
+
+/**
+ * Display subcommand synopsis
+ *
+ * @param progname NUL-terminate C string containing name of program
+ */
+static void
+nsdbparams_list_usage(const char *progname)
+{
+	fprintf(stderr, "\nUsage: %s list [options]\n\n", progname);
+
+	fprintf(stderr, "\t-?, --help           Print this help\n");
+	fprintf(stderr, "\t-d, --debug          Enable debug messages\n");
+	fprintf(stderr, "\t-g, --gid            Run as this effective gid\n");
+	fprintf(stderr, "\t-u, --uid            Run as this effective uid\n");
+
+	fprintf(stderr, "%s", fedfs_gpl_boilerplate);
+}
+
+/**
+ * List all entries in our NSDB connection parameter database
+ *
+ * @param progname NUL-terminate C string containing name of program
+ * @param argc count of command line arguments
+ * @param argv array of NUL-terminated C strings containing command line arguments
+ * @return program exit status
+ */
+int
+nsdbparams_list(const char *progname, int argc, char **argv)
+{
+	char **list, *endptr;
+	FedFsStatus status;
+	unsigned long tmp;
+	struct passwd *pw;
+	struct group *grp;
+	unsigned int i;
+	uid_t uid;
+	gid_t gid;
+	int arg;
+
+	/* Discover the user ID who owns the store */
+	uid = 99;
+	gid = 99;
+	pw = getpwnam(FEDFS_USER);
+	if (pw != NULL) {
+		uid = pw->pw_uid;
+		gid = pw->pw_gid;
+		xlog(D_GENERAL, "Found user %s: UID %u and GID %u",
+			FEDFS_USER, uid, gid);
+	}
+
+	while ((arg = getopt_long(argc, argv, nsdbparams_list_opts,
+					nsdbparams_list_longopts, NULL)) != -1) {
+		switch (arg) {
+		case 'd':
+			xlog_config(D_ALL, 1);
+			xlog_stderr(1);
+			break;
+		case 'g':
+			if (optarg == NULL || *optarg == '\0') {
+				xlog(L_ERROR, "Invalid gid specified");
+				nsdbparams_list_usage(progname);
+				return EXIT_FAILURE;
+			}
+
+			errno = 0;
+			tmp = strtoul(optarg, &endptr, 10);
+			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
+				grp = getgrnam(optarg);
+				if (grp == NULL) {
+					xlog(L_ERROR, "Invalid gid specified");
+					return EXIT_FAILURE;
+				}
+			} else {
+				grp = getgrgid((gid_t)tmp);
+				if (grp == NULL) {
+					xlog(L_ERROR, "Invalid gid specified");
+					return EXIT_FAILURE;
+				}
+			}
+			gid = grp->gr_gid;
+			break;
+		case 'h':
+		case '?':
+			nsdbparams_list_usage(progname);
+			return EXIT_FAILURE;
+		case 'u':
+			if (optarg == NULL || *optarg == '\0') {
+				xlog(L_ERROR, "Invalid uid specified");
+				nsdbparams_list_usage(progname);
+				return EXIT_FAILURE;
+			}
+
+			errno = 0;
+			tmp = strtoul(optarg, &endptr, 10);
+			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
+				pw = getpwnam(optarg);
+				if (pw == NULL) {
+					xlog(L_ERROR, "Invalid uid specified");
+					return EXIT_FAILURE;
+				}
+			} else {
+				pw = getpwuid((uid_t)tmp);
+				if (pw == NULL) {
+					xlog(L_ERROR, "Invalid uid specified");
+					return EXIT_FAILURE;
+				}
+			}
+			uid = pw->pw_uid;
+			gid = pw->pw_gid;
+			break;
+		default:
+			xlog(L_ERROR, "Invalid command line "
+				"argument: %c", (char)arg);
+			nsdbparams_list_usage(progname);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (argc != optind) {
+		xlog(L_ERROR, "Unrecognized command line argument");
+		nsdbparams_list_usage(progname);
+		return EXIT_FAILURE;
+	}
+
+	if (!nsdb_create_basedir())
+		return EXIT_FAILURE;
+
+	if (!nsdbparams_drop_privileges(uid, gid))
+		return EXIT_FAILURE;
+
+	if (!nsdb_init_database())
+		return EXIT_FAILURE;
+
+	status = nsdb_enumerate_nsdbs(&list);
+	switch (status) {
+	case FEDFS_OK:
+		for (i = 0; list[i] != NULL; i++)
+			printf("\t%s\n", list[i]);
+		nsdb_free_string_array(list);
+		break;
+	case FEDFS_ERR_NSDB_PARAMS:
+		printf("The NSDB list is empty.\n");
+		break;
+	default:
+		xlog(L_ERROR, "fedfs_enumerate_nsdbs returned %s",
+			nsdb_display_fedfsstatus(status));
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/src/nsdbparams/main.c b/src/nsdbparams/main.c
index c91ce81..91b0372 100644
--- a/src/nsdbparams/main.c
+++ b/src/nsdbparams/main.c
@@ -4,7 +4,7 @@ 
  */
 
 /*
- * Copyright 2010 Oracle.  All rights reserved.
+ * Copyright 2010, 2011 Oracle.  All rights reserved.
  *
  * This file is part of fedfs-utils.
  *
@@ -33,47 +33,18 @@ 
 #include <stdlib.h>
 #include <stdio.h>
 
-#include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
-#include <getopt.h>
 #include <locale.h>
-#include <netdb.h>
-#include <pwd.h>
 #include <grp.h>
 
-#include <uuid/uuid.h>
 #include <langinfo.h>
 
 #include "fedfs.h"
 #include "nsdb.h"
-#include "junction.h"
 #include "xlog.h"
 #include "gpl-boiler.h"
-
-/**
- * Short form command line options
- */
-static const char nsdbparams_opts[] = "?dD:e:g:l:r:R:f:t:u:";
-
-/**
- * Long form command line options
- */
-static const struct option nsdbparams_longopts[] = {
-	{ "binddn", 1, NULL, 'D', },
-	{ "certfile", 1, NULL, 'f', },
-	{ "debug", 0, NULL, 'd', },
-	{ "gid", 1, NULL, 'g', },
-	{ "help", 0, NULL, '?', },
-	{ "nce", 1, NULL, 'e', },
-	{ "nsdbname", 1, NULL, 'l', },
-	{ "nsdbport", 1, NULL, 'r', },
-	{ "referral", 1, NULL, 'R', },
-	{ "sectype", 1, NULL, 't', },
-	{ "uid", 1, NULL, 'u', },
-	{ NULL, 0, NULL, 0, },
-};
+#include "nsdbparams.h"
 
 /**
  * Display program synopsis
@@ -84,13 +55,15 @@  static void
 nsdbparams_usage(const char *progname)
 {
 	fprintf(stderr, "\n%s: version " VERSION "\n", progname);
-	fprintf(stderr, "Usage: %s [ COMMAND [ ARGUMENTS ]]\n\n", progname);
+	fprintf(stderr, "Usage: %s SUBCOMMAND [ ARGUMENTS ]\n\n", progname);
 
-	fprintf(stderr, "COMMAND is one of:\n");
+	fprintf(stderr, "SUBCOMMAND is one of:\n");
 	fprintf(stderr, "\tdelete     Delete connection parameters\n");
 	fprintf(stderr, "\tlist       Enumerate the store\n");
-	fprintf(stderr, "\tupdate     Update connection parameters\n");
 	fprintf(stderr, "\tshow       Show connection parameters for one NSDB\n");
+	fprintf(stderr, "\tupdate     Update connection parameters\n");
+
+	fprintf(stderr, "\nUse \"%s SUBCOMMAND -?\" for details.\n", progname);
 
 	fprintf(stderr, "%s", fedfs_gpl_boilerplate);
 }
@@ -104,7 +77,7 @@  nsdbparams_usage(const char *progname)
  *
  * Set our effective UID and GID to that of our on-disk cert database.
  */
-static _Bool
+_Bool
 nsdbparams_drop_privileges(const uid_t uid, const gid_t gid)
 {
 	(void)umask(S_IWGRP | S_IWOTH);
@@ -143,277 +116,6 @@  nsdbparams_drop_privileges(const uid_t uid, const gid_t gid)
 }
 
 /**
- * Parse FedFS security type
- *
- * @param arg NUL-terminated string containing input argument
- * @param type OUT: numeric FedFS security type value
- * @return false if could not parse security type
- */
-static _Bool
-nsdbparams_sectype(const char *arg, unsigned int *type)
-{
-	unsigned long tmp;
-	char *endptr;
-
-	errno = 0;
-	tmp = strtoul(arg, &endptr, 10);
-	if (errno != 0 || *endptr != '\0')
-		goto try_symbolic;
-	switch (tmp) {
-	case FEDFS_SEC_NONE:
-	case FEDFS_SEC_TLS:
-		*type = tmp;
-		return true;
-	}
-try_symbolic:
-	if (strcasecmp(arg, "FEDFS_SEC_NONE") == 0) {
-		*type = FEDFS_SEC_NONE;
-		return true;
-	} else if (strcasecmp(arg, "FEDFS_SEC_TLS") == 0) {
-		*type = FEDFS_SEC_TLS;
-		return true;
-	}
-	return false;
-}
-
-/**
- * Delete an NSDB entry in our NSDB connection parameter database
- *
- * @param progname NUL-terminated UTF-8 string containing name of this program
- * @param nsdbname NUL-terminated UTF-8 string containing DNS hostname of target NSDB
- * @param nsdbport IP port number of target NSDB
- * @return a program exit code
- */
-static int
-nsdbparams_delete(const char *progname, const char *nsdbname,
-		const unsigned short nsdbport)
-{
-	FedFsStatus status;
-	nsdb_t host;
-
-	if (nsdbname == NULL) {
-		xlog(L_ERROR, "Missing required command line argument\n");
-		nsdbparams_usage(progname);
-		return EXIT_FAILURE;
-	}
-
-	status = nsdb_lookup_nsdb(nsdbname, nsdbport, &host, NULL);
-	switch (status) {
-	case FEDFS_OK:
-		nsdb_free_nsdb(host);
-		status = nsdb_delete_nsdb(nsdbname, nsdbport);
-		if (status != FEDFS_OK) {
-			xlog(L_ERROR, "nsdb_delete_nsdb returned %s",
-				nsdb_display_fedfsstatus(status));
-			return EXIT_FAILURE;
-		}
-		printf("%s:%u was deleted successfully\n", nsdbname, nsdbport);
-		break;
-	case FEDFS_ERR_NSDB_PARAMS:
-		xlog(L_ERROR, "No record for %s:%u was found",
-			nsdbname, nsdbport);
-		break;
-	default:
-		xlog(L_ERROR, "nsdb_lookup_nsdb returned %s",
-			nsdb_display_fedfsstatus(status));
-		return EXIT_FAILURE;
-	}
-
-	return EXIT_SUCCESS;
-}
-
-/**
- * List all entries in our NSDB connection parameter database
- *
- * @return a program exit code
- */
-static int
-nsdbparams_list(void)
-{
-	FedFsStatus status;
-	unsigned int i;
-	char **list;
-
-	status = nsdb_enumerate_nsdbs(&list);
-	switch (status) {
-	case FEDFS_OK:
-		for (i = 0; list[i] != NULL; i++)
-			printf("\t%s\n", list[i]);
-		nsdb_free_string_array(list);
-		break;
-	case FEDFS_ERR_NSDB_PARAMS:
-		printf("The NSDB list is empty.\n");
-		break;
-	default:
-		xlog(L_ERROR, "fedfs_enumerate_nsdbs returned %s",
-			nsdb_display_fedfsstatus(status));
-		return EXIT_FAILURE;
-	}
-
-	return EXIT_SUCCESS;
-}
-
-/**
- * Update an NSDB entry in our NSDB connection parameter database
- *
- * @param progname NUL-terminated UTF-8 string containing name of this program
- * @param nsdbname NUL-terminated UTF-8 string containing DNS hostname of target NSDB
- * @param nsdbport IP port number of target NSDB
- * @param type new FEDFS_SEC value for target NSDB
- * @param certfile NUL-terminated UTF-8 string containing filename of X.509 cert
- * @param binddn NUL-terminated UTF-8 string containing admin bind DN of target NSDB
- * @param nce NUL-terminated UTF-8 string containing default NCE of target NSDB
- * @param follow_referrals 0 means do not set; 1 means set to false; 2 means set to true
- * @return a program exit code
- */
-static int
-nsdbparams_update(const char *progname, const char *nsdbname,
-		const unsigned short nsdbport, const unsigned int type,
-		const char *certfile, const char *binddn,
-		const char *nce, const _Bool follow_referrals)
-{
-	struct fedfs_secdata secdata = {
-		.type		= type,
-	};
-	unsigned int ldap_err;
-	FedFsStatus retval;
-	int rc;
-
-	rc = EXIT_FAILURE;
-
-	if (nsdbname == NULL) {
-		xlog(L_ERROR, "Missing required command line argument\n");
-		nsdbparams_usage(progname);
-		goto out;
-	}
-
-	retval = nsdb_ping_s(nsdbname, nsdbport, &ldap_err);
-	switch (retval) {
-	case FEDFS_OK:
-		xlog(D_GENERAL, "%s:%u passed ping test", nsdbname, nsdbport);
-		break;
-	case FEDFS_ERR_NSDB_LDAP_VAL:
-		xlog(D_GENERAL, "Failed to ping NSDB %s:%u: %s\n",
-			nsdbname, nsdbport, ldap_err2string(ldap_err));
-		break;
-	default:
-		xlog(L_ERROR, "Warning: %s:%u is not an NSDB: %s",
-			nsdbname, nsdbport, nsdb_display_fedfsstatus(retval));
-	}
-
-	if (type != FEDFS_SEC_NONE) {
-		if (certfile == NULL) {
-			xlog(L_ERROR, "Missing required command line argument\n");
-			nsdbparams_usage(progname);
-			goto out;
-		}
-
-		if (nsdb_read_certfile(certfile, &secdata.data,
-				&secdata.len) != FEDFS_OK) {
-			xlog(L_ERROR, "Failed to read certfile\n");
-			goto out;
-		}
-	}
-
-	/*
-	 * Ensure entry for this NSDB exists before trying to
-	 * update bind DN, NCE, and referral flags for it.
-	 */
-	if (nsdb_update_nsdb(nsdbname, nsdbport, &secdata) == FEDFS_OK) {
-		printf("NSDB list was updated successfully.\n");
-		rc = EXIT_SUCCESS;
-	}
-
-	free(secdata.data);
-
-	if (binddn != NULL)
-		if (nsdb_update_default_binddn(nsdbname, nsdbport,
-						binddn) != FEDFS_OK) {
-			rc = EXIT_FAILURE;
-			goto out;
-		}
-
-	if (nce != NULL)
-		if (nsdb_update_default_nce(nsdbname, nsdbport,
-						nce) != FEDFS_OK) {
-			rc = EXIT_FAILURE;
-			goto out;
-		}
-	if (follow_referrals != 0) {
-		_Bool follow = follow_referrals == 2 ? true : false;
-		if (nsdb_update_follow_referrals(nsdbname, nsdbport,
-						follow) != FEDFS_OK) {
-			rc = EXIT_FAILURE;
-			goto out;
-		}
-	}
-
-out:
-	return rc;
-}
-
-/**
- * Show one NSDB entry in our NSDB connection parameter database
- *
- * @param progname NUL-terminated UTF-8 string containing name of this program
- * @param nsdbname NUL-terminated UTF-8 string containing DNS hostname of target NSDB
- * @param nsdbport IP port number of target NSDB
- * @return a program exit code
- */
-static int
-nsdbparams_show(const char *progname, const char *nsdbname,
-		const unsigned short nsdbport)
-{
-	struct fedfs_secdata secdata = {
-		.type		= 0,
-	};
-	FedFsStatus status;
-	nsdb_t host;
-	char *tmp;
-	int rc;
-
-	rc = EXIT_FAILURE;
-
-	if (nsdbname == NULL) {
-		xlog(L_ERROR, "Missing required command line argument");
-		nsdbparams_usage(progname);
-		goto out;
-	}
-
-	status = nsdb_lookup_nsdb(nsdbname, nsdbport, &host, &secdata);
-	switch (status) {
-	case FEDFS_OK:
-		printf("%s:%u:\n", nsdbname, nsdbport);
-		printf("\tconnection security: %s\n",
-			nsdb_display_fedfsconnectionsec(secdata.type));
-		printf("\tfollow referrals: %s\n",
-			nsdb_follow_referrals(host) ? "yes" : "no");
-		tmp = (char *)nsdb_default_binddn(host);
-		if (tmp != NULL)
-			printf("\tdefault bind DN: %s\n", tmp);
-		tmp = (char *)nsdb_default_nce(host);
-		if (tmp != NULL)
-			printf("\tdefault NCE: %s\n", tmp);
-		nsdb_free_nsdb(host);
-		if (secdata.type != FEDFS_SEC_NONE)
-			printf("secdata:\n%s\n", secdata.data);
-		rc = EXIT_SUCCESS;
-		break;
-	case FEDFS_ERR_NSDB_PARAMS:
-		xlog(L_ERROR, "No record for %s was found", nsdbname);
-		rc = EXIT_SUCCESS;
-		break;
-	default:
-		xlog(L_ERROR, "nsdb_lookup_nsdb returned %s",
-			nsdb_display_fedfsstatus(status));
-		rc = EXIT_FAILURE;
-	}
-
-out:
-	return rc;
-}
-
-/**
  * Program entry point
  *
  * @param argc count of command line arguments
@@ -423,15 +125,8 @@  out:
 int
 main(int argc, char **argv)
 {
-	char *progname, *command, *binddn, *certfile, *nce, *nsdbname, *endptr;
-	int arg, exit_status, follow_referrals;
-	unsigned short nsdbport = LDAP_PORT;
-	unsigned int type = FEDFS_SEC_NONE;
-	unsigned long tmp;
-	struct passwd *pw;
-	struct group *grp;
-	uid_t uid;
-	gid_t gid;
+	int exit_status;
+	char *progname;
 
 	exit_status = EXIT_FAILURE;
 
@@ -447,162 +142,26 @@  main(int argc, char **argv)
 	else
 		progname = argv[0];
 
-	if (argc < 2) {
-		nsdbparams_usage(progname);
-		goto out;
-	}
-
 	/* For the libraries */
 	xlog_stderr(1);
 	xlog_syslog(0);
 	xlog_open(progname);
 
-	/* Discover the user ID who owns the store */
-	uid = 99;
-	gid = 99;
-	pw = getpwnam(FEDFS_USER);
-	if (pw != NULL) {
-		uid = pw->pw_uid;
-		gid = pw->pw_gid;
-		xlog(D_GENERAL, "Found user %s: UID %u and GID %u",
-			FEDFS_USER, uid, gid);
-	}
-
-	nsdbname = nce = certfile = binddn = NULL;
-	follow_referrals = 0;
-
-	/* so that getopt_long(3)'s error messages are meaningful */
-	command = argv[1];
-	argv[1] = argv[0];
-	while ((arg = getopt_long(argc - 1, argv + 1, nsdbparams_opts,
-			nsdbparams_longopts, NULL)) != -1) {
-		switch (arg) {
-		case 'd':
-			xlog_config(D_ALL, 1);
-			xlog_stderr(1);
-			break;
-		case 'D':
-			binddn = optarg;
-			break;
-		case 'e':
-			nce = optarg;
-			break;
-		case 'f':
-			certfile = optarg;
-			break;
-		case 'g':
-			if (optarg == NULL || *optarg == '\0') {
-				fprintf(stderr, "Invalid gid specified");
-				nsdbparams_usage(progname);
-				goto out;
-			}
-
-			errno = 0;
-			tmp = strtoul(optarg, &endptr, 10);
-			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
-				grp = getgrnam(optarg);
-				if (grp == NULL) {
-					fprintf(stderr, "Invalid gid specified");
-					goto out;
-				}
-			} else {
-				grp = getgrgid((gid_t)tmp);
-				if (grp == NULL) {
-					fprintf(stderr, "Invalid gid specified");
-					goto out;
-				}
-			}
-			gid = grp->gr_gid;
-			break;
-		case 'h':
-		case '?':
-			nsdbparams_usage(progname);
-			goto out;
-		case 'l':
-			nsdbname = optarg;
-			break;
-		case 'r':
-			if (!nsdb_parse_port_string(optarg, &nsdbport)) {
-				fprintf(stderr, "Bad port number: %s\n",
-					optarg);
-				nsdbparams_usage(progname);
-				goto out;
-			}
-			break;
-		case 'R':
-			if (strcmp(optarg, "yes") == 0)
-				follow_referrals = 2;
-			else if (strcmp(optarg, "no") == 0)
-				follow_referrals = 1;
-			else {
-				fprintf(stderr, "Bad referral flag: %s\n",
-					optarg);
-				nsdbparams_usage(progname);
-				goto out;
-			}
-			break;
-		case 't':
-			if (!nsdbparams_sectype(optarg, &type)) {
-				fprintf(stderr, "Bad security type: %s\n",
-					optarg);
-				nsdbparams_usage(progname);
-				goto out;
-			}
-			break;
-		case 'u':
-			if (optarg == NULL || *optarg == '\0') {
-				fprintf(stderr, "Invalid uid specified");
-				nsdbparams_usage(progname);
-				goto out;
-			}
-
-			errno = 0;
-			tmp = strtoul(optarg, &endptr, 10);
-			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
-				pw = getpwnam(optarg);
-				if (pw == NULL) {
-					fprintf(stderr, "Invalid uid specified");
-					goto out;
-				}
-			} else {
-				pw = getpwuid((uid_t)tmp);
-				if (pw == NULL) {
-					fprintf(stderr, "Invalid uid specified");
-					goto out;
-				}
-			}
-			uid = pw->pw_uid;
-			gid = pw->pw_gid;
-			break;
-		default:
-			xlog(L_ERROR, "Invalid command line "
-				"argument: %c\n", (char)arg);
-			nsdbparams_usage(progname);
-			goto out;
-		}
-	}
-
-	if (!nsdb_create_basedir())
-		goto out;
-
-	if (!nsdbparams_drop_privileges(uid, gid))
-		goto out;
-
-	if (!nsdb_init_database())
+	if (argc < 2) {
+		nsdbparams_usage(progname);
 		goto out;
+	}
 
-	if (strcasecmp(command, "delete") == 0)
-		exit_status = nsdbparams_delete(progname, nsdbname, nsdbport);
-	else if (strcasecmp(command, "list") == 0)
-		exit_status = nsdbparams_list();
-	else if (strcasecmp(command, "update") == 0)
-		exit_status = nsdbparams_update(progname, nsdbname, nsdbport,
-						type, certfile, binddn, nce,
-						follow_referrals);
-	else if (strcasecmp(command, "show") == 0)
-		exit_status = nsdbparams_show(progname, nsdbname, nsdbport);
+	if (strcasecmp(argv[1], "delete") == 0)
+		exit_status = nsdbparams_delete(progname, argc - 1, argv + 1);
+	else if (strcasecmp(argv[1], "list") == 0)
+		exit_status = nsdbparams_list(progname, argc - 1, argv + 1);
+	else if (strcasecmp(argv[1], "update") == 0)
+		exit_status = nsdbparams_update(progname, argc - 1, argv + 1);
+	else if (strcasecmp(argv[1], "show") == 0)
+		exit_status = nsdbparams_show(progname, argc - 1, argv + 1);
 	else {
-		xlog(L_ERROR, "Unrecognized command\n");
+		xlog(L_ERROR, "Unrecognized subcommand: %s", argv[1]);
 		nsdbparams_usage(progname);
 	}
 
diff --git a/src/nsdbparams/nsdbparams.h b/src/nsdbparams/nsdbparams.h
new file mode 100644
index 0000000..91f71ca
--- /dev/null
+++ b/src/nsdbparams/nsdbparams.h
@@ -0,0 +1,35 @@ 
+/**
+ * @file src/nsdbparams/nsdbparams.h
+ * @brief Declarations and definitions for nsdbparams command line tool
+ */
+
+/*
+ * Copyright 2011 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#ifndef _FEDFS_NSDBPARAMS_H_
+#define _FEDFS_NSDBPARAMS_H_
+
+_Bool	 nsdbparams_drop_privileges(const uid_t uid, const gid_t gid);
+int	 nsdbparams_delete(const char *progname, int argc, char **argv);
+int	 nsdbparams_list(const char *progname, int argc, char **argv);
+int	 nsdbparams_show(const char *progname, int argc, char **argv);
+int	 nsdbparams_update(const char *progname, int argc, char **argv);
+
+#endif	/* !_FEDFS_NSDBPARAMS_H_ */
diff --git a/src/nsdbparams/show.c b/src/nsdbparams/show.c
new file mode 100644
index 0000000..ba52521
--- /dev/null
+++ b/src/nsdbparams/show.c
@@ -0,0 +1,241 @@ 
+/**
+ * @file src/nsdbparams/show.c
+ * @brief Show an item in the local NSDB connection parameters database
+ */
+
+/*
+ * Copyright 2010, 2011 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "fedfs.h"
+#include "nsdb.h"
+#include "junction.h"
+#include "xlog.h"
+#include "gpl-boiler.h"
+#include "nsdbparams.h"
+
+/**
+ * Short form command line options
+ */
+static const char nsdbparams_show_opts[] = "?dg:r:u:";
+
+/**
+ * Long form command line options
+ */
+static const struct option nsdbparams_show_longopts[] = {
+	{ "debug", 0, NULL, 'd', },
+	{ "gid", 1, NULL, 'g', },
+	{ "help", 0, NULL, '?', },
+	{ "nsdbport", 1, NULL, 'r', },
+	{ "uid", 1, NULL, 'u', },
+	{ NULL, 0, NULL, 0, },
+};
+
+/**
+ * Display subcommand synopsis
+ *
+ * @param progname NUL-terminate C string containing name of program
+ */
+static void
+nsdbparams_show_usage(const char *progname)
+{
+	fprintf(stderr, "\nUsage: %s show [options] NSDBNAME\n\n", progname);
+
+	fprintf(stderr, "\t-?, --help           Print this help\n");
+	fprintf(stderr, "\t-d, --debug          Enable debug messages\n");
+	fprintf(stderr, "\t-g, --gid            Run as this effective gid\n");
+	fprintf(stderr, "\t-r, --nsdbport       NSDB port\n");
+	fprintf(stderr, "\t-u, --uid            Run as this effective uid\n");
+
+	fprintf(stderr, "%s", fedfs_gpl_boilerplate);
+}
+
+/**
+ * Show one NSDB entry in our NSDB connection parameter database
+ *
+ * @param progname NUL-terminated UTF-8 string containing name of this program
+ * @param argc count of command line arguments
+ * @param argv array of NUL-terminated C strings containing command line arguments
+ * @return program exit status
+ */
+int
+nsdbparams_show(const char *progname, int argc, char **argv)
+{
+	unsigned short nsdbport = LDAP_PORT;
+	struct fedfs_secdata secdata = {
+		.type		= 0,
+	};
+	char *c, *nsdbname, *endptr;
+	FedFsStatus status;
+	unsigned long tmp;
+	struct passwd *pw;
+	struct group *grp;
+	nsdb_t host;
+	uid_t uid;
+	gid_t gid;
+	int arg;
+
+	/* Discover the user ID who owns the store */
+	uid = 99;
+	gid = 99;
+	pw = getpwnam(FEDFS_USER);
+	if (pw != NULL) {
+		uid = pw->pw_uid;
+		gid = pw->pw_gid;
+		xlog(D_GENERAL, "Found user %s: UID %u and GID %u",
+			FEDFS_USER, uid, gid);
+	}
+
+	while ((arg = getopt_long(argc, argv, nsdbparams_show_opts,
+			nsdbparams_show_longopts, NULL)) != -1) {
+		switch (arg) {
+		case 'd':
+			xlog_config(D_ALL, 1);
+			xlog_stderr(1);
+			break;
+		case 'g':
+			if (optarg == NULL || *optarg == '\0') {
+				xlog(L_ERROR, "Invalid gid specified");
+				nsdbparams_show_usage(progname);
+				return EXIT_FAILURE;
+			}
+
+			errno = 0;
+			tmp = strtoul(optarg, &endptr, 10);
+			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
+				grp = getgrnam(optarg);
+				if (grp == NULL) {
+					xlog(L_ERROR, "Invalid gid specified");
+					return EXIT_FAILURE;
+				}
+			} else {
+				grp = getgrgid((gid_t)tmp);
+				if (grp == NULL) {
+					xlog(L_ERROR, "Invalid gid specified");
+					return EXIT_FAILURE;
+				}
+			}
+			gid = grp->gr_gid;
+			break;
+		case 'h':
+		case '?':
+			nsdbparams_show_usage(progname);
+			return EXIT_FAILURE;
+		case 'r':
+			if (!nsdb_parse_port_string(optarg, &nsdbport)) {
+				xlog(L_ERROR, "Bad port number: %s\n",
+					optarg);
+				nsdbparams_show_usage(progname);
+				return EXIT_FAILURE;
+			}
+			break;
+		case 'u':
+			if (optarg == NULL || *optarg == '\0') {
+				xlog(L_ERROR, "Invalid uid specified");
+				nsdbparams_show_usage(progname);
+				return EXIT_FAILURE;
+			}
+
+			errno = 0;
+			tmp = strtoul(optarg, &endptr, 10);
+			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
+				pw = getpwnam(optarg);
+				if (pw == NULL) {
+					xlog(L_ERROR, "Invalid uid specified");
+					return EXIT_FAILURE;
+				}
+			} else {
+				pw = getpwuid((uid_t)tmp);
+				if (pw == NULL) {
+					xlog(L_ERROR, "Invalid uid specified");
+					return EXIT_FAILURE;
+				}
+			}
+			uid = pw->pw_uid;
+			gid = pw->pw_gid;
+			break;
+		default:
+			xlog(L_ERROR, "Invalid command line "
+				"argument: %c", (char)arg);
+			nsdbparams_show_usage(progname);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (!nsdb_create_basedir())
+		return EXIT_FAILURE;
+
+	if (!nsdbparams_drop_privileges(uid, gid))
+		return EXIT_FAILURE;
+
+	if (!nsdb_init_database())
+		return EXIT_FAILURE;
+
+	if (argc == optind + 1)
+		nsdbname = argv[optind];
+	else if (argc > optind + 1) {
+		xlog(L_ERROR, "Unrecognized positional parameters");
+		nsdbparams_show_usage(progname);
+		return EXIT_FAILURE;
+	} else {
+		xlog(L_ERROR, "No NSDB hostname was specified");
+		nsdbparams_show_usage(progname);
+		return EXIT_FAILURE;
+	}
+
+	status = nsdb_lookup_nsdb(nsdbname, nsdbport, &host, &secdata);
+	switch (status) {
+	case FEDFS_OK:
+		printf("%s:%u:\n", nsdbname, nsdbport);
+		printf("\tconnection security: %s\n",
+			nsdb_display_fedfsconnectionsec(secdata.type));
+		printf("\tfollow referrals: %s\n",
+			nsdb_follow_referrals(host) ? "yes" : "no");
+		c = (char *)nsdb_default_binddn(host);
+		if (c != NULL)
+			printf("\tdefault bind DN: %s\n", c);
+		c = (char *)nsdb_default_nce(host);
+		if (c != NULL)
+			printf("\tdefault NCE: %s\n", c);
+		nsdb_free_nsdb(host);
+		if (secdata.type != FEDFS_SEC_NONE)
+			printf("secdata:\n%s\n", secdata.data);
+		break;
+	case FEDFS_ERR_NSDB_PARAMS:
+		xlog(L_ERROR, "No record for %s was found", nsdbname);
+		break;
+	default:
+		xlog(L_ERROR, "nsdb_lookup_nsdb returned %s",
+			nsdb_display_fedfsstatus(status));
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/src/nsdbparams/update.c b/src/nsdbparams/update.c
new file mode 100644
index 0000000..549f9cc
--- /dev/null
+++ b/src/nsdbparams/update.c
@@ -0,0 +1,371 @@ 
+/**
+ * @file src/nsdbparams/update.c
+ * @brief Update an item in the local NSDB connection parameters database
+ */
+
+/*
+ * Copyright 2010, 2011 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <locale.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "fedfs.h"
+#include "nsdb.h"
+#include "xlog.h"
+#include "gpl-boiler.h"
+#include "nsdbparams.h"
+
+/**
+ * Short form command line options
+ */
+static const char nsdbparams_update_opts[] = "?dD:e:g:l:r:R:f:t:u:";
+
+/**
+ * Long form command line options
+ */
+static const struct option nsdbparams_update_longopts[] = {
+	{ "binddn", 1, NULL, 'D', },
+	{ "certfile", 1, NULL, 'f', },
+	{ "debug", 0, NULL, 'd', },
+	{ "gid", 1, NULL, 'g', },
+	{ "help", 0, NULL, '?', },
+	{ "nce", 1, NULL, 'e', },
+	{ "nsdbname", 1, NULL, 'l', },
+	{ "nsdbport", 1, NULL, 'r', },
+	{ "referral", 1, NULL, 'R', },
+	{ "sectype", 1, NULL, 't', },
+	{ "uid", 1, NULL, 'u', },
+	{ NULL, 0, NULL, 0, },
+};
+
+/**
+ * Display subcommand synopsis
+ *
+ * @param progname NUL-terminate C string containing name of program
+ */
+static void
+nsdbparams_update_usage(const char *progname)
+{
+	fprintf(stderr, "\nUsage: %s update [options] NSDBNAME\n\n", progname);
+
+	fprintf(stderr, "\t-?, --help           Print this help\n");
+	fprintf(stderr, "\t-d, --debug          Enable debug messages\n");
+	fprintf(stderr, "\t-D, --binddn         Default bind DN\n");
+	fprintf(stderr, "\t-e, --nce            Default DN of NCE\n");
+	fprintf(stderr, "\t-f, --certfile       Pathname to server certificate\n");
+	fprintf(stderr, "\t-g, --gid            Run as this effective gid\n");
+	fprintf(stderr, "\t-r, --nsdbport       NSDB port\n");
+	fprintf(stderr, "\t-R, --referral       Toggle follow-referral flag\n");
+	fprintf(stderr, "\t-t, --sectype        Sectype for this NSDB\n");
+	fprintf(stderr, "\t-u, --uid            Run as this effective uid\n\n");
+
+	fprintf(stderr, "\tSECTYPE is one of 0, 1, none, tls, "
+			"FEDFS_SEC_NONE, or FEDFS_SEC_TLS\n");
+
+	fprintf(stderr, "%s", fedfs_gpl_boilerplate);
+}
+
+/**
+ * Parse FedFS security type
+ *
+ * @param arg NUL-terminated string containing input argument
+ * @param type OUT: numeric FedFS security type value
+ * @return false if could not parse security type
+ */
+static _Bool
+nsdbparams_sectype(const char *arg, unsigned int *type)
+{
+	unsigned long tmp;
+	char *endptr;
+
+	errno = 0;
+	tmp = strtoul(arg, &endptr, 10);
+	if (errno != 0 || *endptr != '\0')
+		goto try_symbolic;
+	switch (tmp) {
+	case FEDFS_SEC_NONE:
+	case FEDFS_SEC_TLS:
+		*type = tmp;
+		return true;
+	}
+try_symbolic:
+	if (strcasecmp(arg, "FEDFS_SEC_NONE") == 0) {
+		*type = FEDFS_SEC_NONE;
+		return true;
+	} else if (strcasecmp(arg, "none") == 0) {
+		*type = FEDFS_SEC_NONE;
+		return true;
+	} else if (strcasecmp(arg, "FEDFS_SEC_TLS") == 0) {
+		*type = FEDFS_SEC_TLS;
+		return true;
+	} else if (strcasecmp(arg, "tls") == 0) {
+		*type = FEDFS_SEC_TLS;
+		return true;
+	}
+	return false;
+}
+
+/**
+ * Update an NSDB entry in our NSDB connection parameter database
+ *
+ * @param progname NUL-terminated UTF-8 string containing name of this program
+ * @param argc count of command line arguments
+ * @param argv array of NUL-terminated C strings containing command line arguments
+ * @return program exit status
+ */
+int
+nsdbparams_update(const char *progname, int argc, char **argv)
+{
+	char *binddn, *certfile, *nce, *nsdbname, *endptr;
+	unsigned short nsdbport = LDAP_PORT;
+	unsigned int type = FEDFS_SEC_NONE;
+	struct fedfs_secdata secdata = {
+		.type		= type,
+	};
+	int arg, follow_referrals;
+	unsigned int ldap_err;
+	FedFsStatus retval;
+	unsigned long tmp;
+	struct passwd *pw;
+	struct group *grp;
+	uid_t uid;
+	gid_t gid;
+	int rc;
+
+	/* Discover the user ID who owns the store */
+	uid = 99;
+	gid = 99;
+	pw = getpwnam(FEDFS_USER);
+	if (pw != NULL) {
+		uid = pw->pw_uid;
+		gid = pw->pw_gid;
+		xlog(D_GENERAL, "Found user %s: UID %u and GID %u",
+			FEDFS_USER, uid, gid);
+	}
+
+	rc = EXIT_FAILURE;
+	nce = certfile = binddn = NULL;
+	follow_referrals = 0;
+
+	/* so that getopt_long(3)'s error messages are meaningful */
+	while ((arg = getopt_long(argc, argv, nsdbparams_update_opts,
+			nsdbparams_update_longopts, NULL)) != -1) {
+		switch (arg) {
+		case 'd':
+			xlog_config(D_ALL, 1);
+			xlog_stderr(1);
+			break;
+		case 'D':
+			binddn = optarg;
+			break;
+		case 'e':
+			nce = optarg;
+			break;
+		case 'f':
+			certfile = optarg;
+			break;
+		case 'g':
+			if (optarg == NULL || *optarg == '\0') {
+				xlog(L_ERROR, "Invalid gid specified");
+				nsdbparams_update_usage(progname);
+				goto out;
+			}
+
+			errno = 0;
+			tmp = strtoul(optarg, &endptr, 10);
+			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
+				grp = getgrnam(optarg);
+				if (grp == NULL) {
+					xlog(L_ERROR, "Invalid gid specified");
+					goto out;
+				}
+			} else {
+				grp = getgrgid((gid_t)tmp);
+				if (grp == NULL) {
+					xlog(L_ERROR, "Invalid gid specified");
+					goto out;
+				}
+			}
+			gid = grp->gr_gid;
+			break;
+		case 'h':
+		case '?':
+			nsdbparams_update_usage(progname);
+			goto out;
+		case 'l':
+			nsdbname = optarg;
+			break;
+		case 'r':
+			if (!nsdb_parse_port_string(optarg, &nsdbport)) {
+				xlog(L_ERROR, "Bad port number: %s",
+					optarg);
+				nsdbparams_update_usage(progname);
+				goto out;
+			}
+			break;
+		case 'R':
+			if (strcmp(optarg, "yes") == 0)
+				follow_referrals = 2;
+			else if (strcmp(optarg, "no") == 0)
+				follow_referrals = 1;
+			else {
+				xlog(L_ERROR, "Bad referral flag: %s",
+					optarg);
+				nsdbparams_update_usage(progname);
+				goto out;
+			}
+			break;
+		case 't':
+			if (!nsdbparams_sectype(optarg, &type)) {
+				xlog(L_ERROR, "Bad security type: %s",
+					optarg);
+				nsdbparams_update_usage(progname);
+				goto out;
+			}
+			break;
+		case 'u':
+			if (optarg == NULL || *optarg == '\0') {
+				xlog(L_ERROR, "Invalid uid specified");
+				nsdbparams_update_usage(progname);
+				goto out;
+			}
+
+			errno = 0;
+			tmp = strtoul(optarg, &endptr, 10);
+			if (errno != 0 || *endptr != '\0' || tmp > UINT_MAX) {
+				pw = getpwnam(optarg);
+				if (pw == NULL) {
+					xlog(L_ERROR, "Invalid uid specified");
+					goto out;
+				}
+			} else {
+				pw = getpwuid((uid_t)tmp);
+				if (pw == NULL) {
+					xlog(L_ERROR, "Invalid uid specified");
+					goto out;
+				}
+			}
+			uid = pw->pw_uid;
+			gid = pw->pw_gid;
+			break;
+		default:
+			xlog(L_ERROR, "Invalid command line "
+				"argument: %c\n", (char)arg);
+			nsdbparams_update_usage(progname);
+			goto out;
+		}
+	}
+
+	if (argc == optind + 1)
+		nsdbname = argv[optind];
+	else if (argc > optind + 1) {
+		xlog(L_ERROR, "Unrecognized positional parameters");
+		nsdbparams_update_usage(progname);
+		goto out;
+	} else {
+		xlog(L_ERROR, "No NSDB hostname was specified");
+		nsdbparams_update_usage(progname);
+		goto out;
+	}
+
+	if (!nsdb_create_basedir())
+		goto out;
+
+	if (!nsdbparams_drop_privileges(uid, gid))
+		goto out;
+
+	if (!nsdb_init_database())
+		goto out;
+
+	printf("Pinging NSDB %s:%u...", nsdbname, nsdbport);
+	retval = nsdb_ping_s(nsdbname, nsdbport, &ldap_err);
+	switch (retval) {
+	case FEDFS_OK:
+		xlog(D_GENERAL, "%s:%u passed ping test", nsdbname, nsdbport);
+		break;
+	case FEDFS_ERR_NSDB_LDAP_VAL:
+		xlog(L_WARNING, "Failed to ping NSDB %s:%u: %s",
+			nsdbname, nsdbport, ldap_err2string(ldap_err));
+		break;
+	default:
+		xlog(L_WARNING, "Warning: %s:%u is not an NSDB: %s",
+			nsdbname, nsdbport, nsdb_display_fedfsstatus(retval));
+	}
+
+	if (type != FEDFS_SEC_NONE) {
+		if (certfile == NULL) {
+			xlog(L_ERROR, "No certfile was specified");
+			nsdbparams_update_usage(progname);
+			goto out;
+		}
+
+		if (nsdb_read_certfile(certfile, &secdata.data,
+				&secdata.len) != FEDFS_OK) {
+			xlog(L_ERROR, "Failed to read certfile");
+			goto out;
+		}
+	}
+
+	/*
+	 * Ensure entry for this NSDB exists before trying to
+	 * update bind DN, NCE, and referral flags for it.
+	 */
+	if (nsdb_update_nsdb(nsdbname, nsdbport, &secdata) == FEDFS_OK) {
+		printf("NSDB list was updated successfully.\n");
+		rc = EXIT_SUCCESS;
+	}
+
+	free(secdata.data);
+
+	if (binddn != NULL)
+		if (nsdb_update_default_binddn(nsdbname, nsdbport,
+						binddn) != FEDFS_OK) {
+			rc = EXIT_FAILURE;
+			goto out;
+		}
+
+	if (nce != NULL)
+		if (nsdb_update_default_nce(nsdbname, nsdbport,
+						nce) != FEDFS_OK) {
+			rc = EXIT_FAILURE;
+			goto out;
+		}
+	if (follow_referrals != 0) {
+		_Bool follow = follow_referrals == 2 ? true : false;
+		if (nsdb_update_follow_referrals(nsdbname, nsdbport,
+						follow) != FEDFS_OK) {
+			rc = EXIT_FAILURE;
+			goto out;
+		}
+	}
+
+out:
+	return rc;
+}