Patchwork [1/3] nsdbc: Introduce a tool for creating a simple NSDB.

login
register
mail settings
Submitter Chuck Lever
Date Dec. 6, 2012, 5:01 p.m.
Message ID <20121206170157.5988.36008.stgit@seurat.1015granger.net>
Download mbox | patch
Permalink /patch/204287/
State Accepted
Headers show

Comments

Chuck Lever - Dec. 6, 2012, 5:01 p.m.
NCEs like "o=fedfs", where the NCE and the naming context are the
same entry, are allowed and supported.  But we need something that
makes it easy for a novice to get started.

Add a new tool called nsdb-simple-nce that creates an entry as a
child of some parent DN (usually the LDAP server's domain naming
context, ie. dc=example,dc=net), and then makes that new entry an
NCE for its naming context.

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

 .gitignore                  |    1 
 doc/man/Makefile.am         |    2 
 doc/man/nsdb-simple-nce.8   |  292 +++++++++++++++++++++++++++++++++++++++++++
 src/include/nsdb.h          |    6 +
 src/libnsdb/administrator.c |  182 +++++++++++++++++++++++++++
 src/nsdbc/Makefile.am       |    2 
 src/nsdbc/nsdb-simple-nce.c |  249 +++++++++++++++++++++++++++++++++++++
 7 files changed, 732 insertions(+), 2 deletions(-)
 create mode 100644 doc/man/nsdb-simple-nce.8
 create mode 100644 src/nsdbc/nsdb-simple-nce.c

Patch

diff --git a/.gitignore b/.gitignore
index 57e67c0..09276be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,7 @@  nsdb-describe
 nsdb-list
 nsdb-nces
 nsdb-resolve-fsn
+nsdb-simple-nce
 nsdb-update-fsl
 nsdb-update-nci
 nsdb-remove-nci
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 30016ce..8d63207 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -34,7 +34,7 @@  NSDB_CLIENT_CMDS	= nsdb-create-fsl.8 nsdb-create-fsn.8 \
 			  nsdb-annotate.8 nsdb-describe.8 \
 			  nsdb-list.8 nsdb-nces.8 \
 			  nsdb-update-nci.8 nsdb-remove-nci.8 \
-			  nsdb-delete-nsdb.8
+			  nsdb-delete-nsdb.8 nsdb-simple-nce.8
 
 dist_man7_MANS		= fedfs.7
 dist_man8_MANS		= rpc.fedfsd.8 mount.fedfs.8 fedfs-map-nfs4.8 nfsref.8 \
diff --git a/doc/man/nsdb-simple-nce.8 b/doc/man/nsdb-simple-nce.8
new file mode 100644
index 0000000..24c0cdb
--- /dev/null
+++ b/doc/man/nsdb-simple-nce.8
@@ -0,0 +1,292 @@ 
+.\"@(#)nsdb-simple-nce.8"
+.\"
+.\" @file doc/man/nsdb-simple-nce.8
+.\" @brief man page for nsdb-simple-nce client command
+.\"
+
+.\"
+.\" Copyright 2012 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
+.\"
+.TH NSDB-SIMPLE-NCE 8 "@publication-date@"
+.SH NAME
+nsdb-simple-nce \- Create a simple NSDB Container Entry
+.SH SYNOPSIS
+.B nsdb-simple-nce
+.RB [ \-?d ]
+.RB [ \-D
+.IR binddn ]
+.RB [ \-l
+.IR nsdbname ]
+.RB [ \-r
+.IR nsdbport ]
+.I parent-dn
+.SH INTRODUCTION
+RFC 5716 introduces the Federated File System (FedFS, for short).
+FedFS is an extensible standardized mechanism
+by which system administrators construct
+a coherent namespace across multiple file servers using
+.IR "file system referrals" .
+For further details, see
+.BR fedfs (7).
+.P
+The bulk of FedFS junction information in a FedFS domain is stored
+on one or more LDAP servers.
+These servers are known as
+.IR "namespace databases" ,
+or NSDBs, for short.
+.P
+FedFS-enabled file servers and clients access the information stored
+on NSDBs via standard LDAP queries.
+FedFS-enabled file servers use these queries to resolve FedFS junctions.
+FedFS administrators use these queries to manage information
+about file sets contained in a FedFS domain name space.
+.SH DESCRIPTION
+The
+.BR nsdb-simple-nce (8)
+command is part of a collection of low-level single-use programs that are
+intended for testing the NSDB protocol or for use in scripts.
+This command is an easy way to turn a standard LDAP server into an NSDB
+by creating an NSDB Container Entry in the server's
+.I Directory Information Tree
+(or DIT, for short).
+.P
+The top of the DIT on an LDAP server has one or more
+.IR "naming contexts" .
+Some LDAP server implementations call these contexts
+.IR "root suffixes" .
+An LDAP server's naming contexts are easy for clients to locate
+with a well-known search query.
+All LDAP entries on that server are contained under naming contexts.
+.P
+The LDAP entry under which all other FedFS-related entries reside
+is known as the
+.I NSDB Container Entry
+(or NCE).
+The NCE can be a naming context entry,
+or it can be located somewhere below a naming context.
+The
+.BR nsdb-simple-nce (8)
+command adds an NSDB Container Entry
+using a simple NCE DN that can be created without
+much prior knowledge of the server's DIT.
+.P
+Once this entry is created, the
+.BR nsdb-simple-nce (8)
+command automatically adds the new entry's DN to the parent
+naming context so that NSDB clients can find it.
+The result is a ready-to-use NSDB.
+.P
+The
+.BR nsdb-simple-nce (8)
+command establishes an NSDB quickly and without fuss.
+A more sophisticated configuration may be
+established using standard LDAP tools and the
+.BR nsdb-update-nci (8)
+command.
+This might be necessary when preparing an existing LDAP server
+with a large pre-existing DIT for use as an NSDB.
+.P
+This command has one positional parameter which specifies
+the distinguished name of the parent entry of the new
+NSDB Container Entry.
+.SH OPTIONS
+.IP "\fB\-d, \-\-debug"
+Enables debugging messages during operation.
+.IP "\fB\-?, \-\-help"
+Displays
+.BR nsdb-simple-nce (8)
+version information and a usage message on
+.IR stderr .
+.IP "\fB-D, \-\-binddn=\fIbind-distinguished-name\fP"
+Specifies a distinguished name of an entity used to bind to the LDAP server
+where the NSDB resides.
+If the
+.B \-\-binddn
+option is not specified,
+the value of the FEDFS_NSDB_ADMIN environment variable is consulted.
+If this variable is not set,
+the NSDB connection parameter database is searched for this DN.
+If none of these is specified, or
+if this entity does not have permission to modify this area
+of the server's DIT, the
+.BR nsdb-simple-nce (8)
+command fails.
+.IP "\fB\-l, \-\-nsdbname=\fINSDB-hostname\fP"
+Specifies the hostname of the NSDB where the target NCE should reside.
+If the
+.B \-\-nsdbname
+option is not specified,
+the value of the FEDFS_NSDB_HOST environment variable is consulted.
+If the variable is not set and the
+.B \-\-nsdbname
+option is not specified, the
+.BR nsdb-simple-nce (8)
+command fails.
+.IP "\fB\-r, \-\-nsdbport=\fINSDB-port\fP"
+Specifies the IP port of the NSDB where the target NCE should reside.
+If the
+.B \-\-nsdbport
+option is not specified,
+the value of the FEDFS_NSDB_PORT environment variable is consulted.
+The default value if the variable is not set is 389.
+.SH EXIT CODES
+The NSDB returns a value that reflects the success of the requested operation.
+.TP
+.B FEDFS_OK
+The LDAP modify request succeeded.
+.TP
+.B FEDFS_ERR_ACCESS
+The bound entity does not have permission to perform the requested operation.
+.TP
+.B FEDFS_ERR_INVAL
+One of the arguments was not valid.
+.TP
+.B FEDFS_ERR_SVRFAULT
+An unanticipated non-protocol error occurred.
+.TP
+.B FEDFS_ERR_NSDB_ROUTE
+The
+.BR nsdb-simple-nce (8)
+command was unable to find a route to the specified NSDB.
+.TP
+.B FEDFS_ERR_NSDB_DOWN
+The
+.BR nsdb-simple-nce (8)
+command determined that the specified NSDB was down.
+.TP
+.B FEDFS_ERR_NSDB_CONN
+The
+.BR nsdb-simple-nce (8)
+command was unable to establish a connection with the specified NSDB.
+.TP
+.B FEDFS_ERR_NSDB_AUTH
+The
+.BR nsdb-simple-nce (8)
+command was unable to authenticate
+and establish a secure connection with the specified NSDB.
+.TP
+.B FEDFS_ERR_NSDB_LDAP
+A non-specific LDAP error occurred on the connection between the
+.BR nsdb-simple-nce (8)
+command and specified NSDB.
+.TP
+.B FEDFS_ERR_NSDB_LDAP_VAL
+An LDAP error occurred on the connection between the
+.BR nsdb-simple-nce (8)
+command and specified NSDB.
+The specific error may be displayed on the command line.
+.TP
+.B FEDFS_ERR_NSDB_RESPONSE
+The
+.BR nsdb-simple-nce (8)
+command received a malformed response from the specified NSDB.
+.TP
+.B FEDFS_ERR_NSDB_FAULT
+An unanticipated error related to the specified NSDB occurred.
+.TP
+.B FEDFS_ERR_NSDB_PARAMS
+The local NSDB connection parameter database
+does not have any connection parameters on record for the specified NSDB.
+.TP
+.B FEDFS_ERR_NSDB_LDAP_REFERRAL
+The
+.BR nsdb-simple-nce (8)
+command received an LDAP referral that it was unable to follow.
+.TP
+.B FEDFS_ERR_NSDB_LDAP_REFERRAL_VAL
+The
+.BR nsdb-simple-nce (8)
+command received an LDAP referral that it was unable to follow.
+A specific error may be displayed on the command line.
+.TP
+.B FEDFS_ERR_NSDB_LDAP_REFERRAL_NOTFOLLOWED
+The
+.BR nsdb-simple-nce (8)
+command received an LDAP referral that it chose not to follow,
+either because the local implementation does not support
+following LDAP referrals or LDAP referral following is disabled.
+.TP
+.B FEDFS_ERR_NSDB_PARAMS_LDAP_REFERRAL
+The
+.BR nsdb-simple-nce (8)
+command received an LDAP referral that it chose not to follow
+because the local NSDB connection parameter database had no
+connection parameters for the NSDB targeted by the LDAP referral.
+.SH EXAMPLES
+Suppose you are the FedFS administrator of the
+.I example.net
+FedFS domain and that you want to make the LDAP server
+.IR ldap.example.net
+into an NSDB.
+Ensure the LDAP server has the FedFS schema installed.
+The naming context "dc=example,dc=net" must exist, and
+must have an entry associated with it.
+Then you might use:
+.RS
+.sp
+$ nsdb-simple-nce -l ldap.example.net -D cn=Manager dc=example,dc=net
+.br
+Enter NSDB password:
+.br
+Successfully created simple NCE
+.sp
+.RE
+The distinguished name of the new NCE is "ou=fedfs,dc=example,dc=net".
+The naming context "dc=example,dc=net" is updated to refer NSDB clients
+to the "ou=fedfs,dc=example,dc=net" entry.
+.P
+To see the new NCE, use
+.BR nsdb-nces (8).
+.SH SECURITY
+LDAP naming contexts are typically writable only by administrative entities.
+The
+.BR nsdb-simple-nce (8)
+command must bind as an administrative entity to perform this operation.
+The
+.BR nsdb-simple-nce (8)
+command asks for a password on
+.IR stdin .
+Standard password blanking techniques are used
+to obscure the password on the user's terminal.
+.P
+The target LDAP server must be registered in the local NSDB connection
+parameter database.
+The connection security mode listed
+in the NSDB connection parameter database
+for the target LDAP server is used during this operation.
+See
+.BR nsdbparams (8)
+for details on how to register an NSDB
+in the local NSDB connection parameter database.
+.SH "SEE ALSO"
+.BR fedfs (7),
+.BR nsdb-nces (8),
+.BR nsdb-update-nce (8),
+.BR nsdbparams (8)
+.sp
+RFC 5716 for FedFS requirements and overview
+.sp
+RFC 4510 for an introduction to LDAP
+.SH COLOPHON
+This page is part of the fedfs-utils package.
+A description of the project and information about reporting bugs
+can be found at
+.IR http://wiki.linux-nfs.org/wiki/index.php/FedFsUtilsProject .
+.SH "AUTHOR"
+Chuck Lever <chuck.lever@oracle.com>
diff --git a/src/include/nsdb.h b/src/include/nsdb.h
index 9d4468b..030b1d2 100644
--- a/src/include/nsdb.h
+++ b/src/include/nsdb.h
@@ -303,6 +303,12 @@  FedFsStatus	 nsdb_update_fsl_s(nsdb_t host, const char *nce,
  **/
 
 /**
+ * Create a simple "ou=fedfs" entry
+ */
+FedFsStatus	 nsdb_create_simple_nce_s(nsdb_t host, const char *parent,
+				char **dn, unsigned int *ldap_err);
+
+/**
  * Update or remove NSDB container information
  */
 FedFsStatus	 nsdb_update_nci_s(nsdb_t host, const char *nce,
diff --git a/src/libnsdb/administrator.c b/src/libnsdb/administrator.c
index 802afcf..a664fa9 100644
--- a/src/libnsdb/administrator.c
+++ b/src/libnsdb/administrator.c
@@ -1227,6 +1227,188 @@  nsdb_update_fsl_s(nsdb_t host, const char *nce, const char *fsl_uuid,
 }
 
 /**
+ * Add a new top-level o=fedfs entry
+ *
+ * @param ld an initialized LDAP server descriptor
+ * @param dn OUT: a NUL-terminated C string containing DN of new NCE
+ * @param ldap_err OUT: possibly an LDAP error code
+ * @return a FedFsStatus code
+ *
+ * Caller must free "dn" with ber_memfree(3).
+ *
+ * LDIF equivalent:
+ *
+ * @verbatim
+
+   dn: o=fedfs
+   changeType: add
+   objectClass: organization
+   o: fedfs
+   @endverbatim
+ */
+static FedFsStatus
+nsdb_create_nce_add_top_entry(LDAP *ld, char **dn,
+		unsigned int *ldap_err)
+{
+	char *ocvals[2], *ouvals[2];
+	LDAPMod *attrs[3];
+	LDAPMod attr[2];
+	size_t len;
+	int i, rc;
+	char *nce;
+
+	for (i = 0; i < 3; i++)
+		attrs[i] = &attr[i];
+	i = 0;
+
+	nsdb_init_add_attribute(attrs[i++],
+				"objectClass", ocvals, "organization");
+	nsdb_init_add_attribute(attrs[i++],
+				"o", ouvals, "fedfs");
+	attrs[i] = NULL;
+
+	len = strlen("o=fedfs");
+	nce = ber_memalloc(len);
+	if (nce == NULL) {
+		xlog(D_GENERAL, "%s: No memory for NCE DN", __func__);
+		return FEDFS_ERR_SVRFAULT;
+	}
+	(void)sprintf(nce, "o=fedfs");
+
+	xlog(D_CALL, "%s: Using DN '%s'", __func__, nce);
+	rc = ldap_add_ext_s(ld, nce, attrs, NULL, NULL);
+	if (rc != LDAP_SUCCESS) {
+		ber_memfree(nce);
+		xlog(D_GENERAL, "Failed to add new blank NCE: %s",
+				ldap_err2string(rc));
+		*ldap_err = rc;
+		return FEDFS_ERR_NSDB_LDAP_VAL;
+	}
+
+	*dn = nce;
+	xlog(D_CALL, "%s: Successfully added blank NCE", __func__);
+	return FEDFS_OK;
+}
+
+/**
+ * Add a new ou=fedfs entry under "parent"
+ *
+ * @param ld an initialized LDAP server descriptor
+ * @param parent a NUL-terminated C string containing DN of parent
+ * @param dn OUT: a NUL-terminated C string containing DN of new NCE
+ * @param ldap_err OUT: possibly an LDAP error code
+ * @return a FedFsStatus code
+ *
+ * Caller must free "dn" with ber_memfree(3).
+ *
+ * LDIF equivalent:
+ *
+ * @verbatim
+
+   dn: ou=fedfs,"parent"
+   changeType: add
+   objectClass: organizationalUnit
+   ou: fedfs
+   @endverbatim
+ */
+static FedFsStatus
+nsdb_create_nce_add_entry(LDAP *ld, const char *parent, char **dn,
+		unsigned int *ldap_err)
+{
+	char *ocvals[2], *ouvals[2];
+	LDAPMod *attrs[3];
+	LDAPMod attr[2];
+	size_t len;
+	int i, rc;
+	char *nce;
+
+	for (i = 0; i < 3; i++)
+		attrs[i] = &attr[i];
+	i = 0;
+
+	nsdb_init_add_attribute(attrs[i++],
+				"objectClass", ocvals, "organizationalUnit");
+	nsdb_init_add_attribute(attrs[i++],
+				"ou", ouvals, "fedfs");
+	attrs[i] = NULL;
+
+	len = strlen("ou=fedfs,") + strlen(parent) + 1;
+	nce = ber_memalloc(len);
+	if (nce == NULL) {
+		xlog(D_GENERAL, "%s: No memory for NCE DN", __func__);
+		return FEDFS_ERR_SVRFAULT;
+	}
+	(void)sprintf(nce, "ou=fedfs,%s", parent);
+
+	xlog(D_CALL, "%s: Using DN '%s'", __func__, nce);
+	rc = ldap_add_ext_s(ld, nce, attrs, NULL, NULL);
+	if (rc != LDAP_SUCCESS) {
+		ber_memfree(nce);
+		xlog(D_GENERAL, "%s: Failed to add new blank NCE: %s",
+				__func__, ldap_err2string(rc));
+		*ldap_err = rc;
+		return FEDFS_ERR_NSDB_LDAP_VAL;
+	}
+
+	*dn = nce;
+	xlog(D_CALL, "%s: Successfully added blank NCE", __func__);
+	return FEDFS_OK;
+}
+
+/**
+ * Create a blank NSDB container entry on a target NSDB server
+ *
+ * @param host an initialized and bound nsdb_t object
+ * @param parent a NUL-terminated C string containing DN of parent
+ * @param dn OUT: a NUL-terminated C string containing DN of new NCE
+ * @param ldap_err OUT: possibly an LDAP error code
+ * @return a FedFsStatus code
+ *
+ * Caller must free "dn" with free(3).
+ *
+ * Note: an NCE can be any entry in an LDAP DIT.  This function creates
+ * the simple case of an "ou=fedfs" entry under some other entry.
+ */
+FedFsStatus
+nsdb_create_simple_nce_s(nsdb_t host, const char *parent,
+		char **dn, unsigned int *ldap_err)
+{
+	FedFsStatus retval;
+	char *nce;
+
+	if (host->fn_ldap == NULL) {
+		xlog(L_ERROR, "%s: NSDB not open", __func__);
+		return FEDFS_ERR_INVAL;
+	}
+
+	if (parent == NULL || ldap_err == NULL) {
+		xlog(L_ERROR, "%s: Invalid parameter", __func__);
+		return FEDFS_ERR_INVAL;
+	}
+
+	if (parent[0] == '\0')
+		retval = nsdb_create_nce_add_top_entry(host->fn_ldap,
+							&nce, ldap_err);
+	else
+		retval = nsdb_create_nce_add_entry(host->fn_ldap, parent,
+							&nce, ldap_err);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	retval = FEDFS_OK;
+	if (dn != NULL) {
+		*dn = strdup(nce);
+		if (*dn == NULL) {
+			xlog(D_GENERAL, "%s: No memory for DN",
+				__func__);
+			retval = FEDFS_ERR_SVRFAULT;
+		}
+	}
+	ber_memfree(nce);
+	return retval;
+}
+
+/**
  * Update NSDB Container Info in a namingContext entry
  *
  * @param ld an initialized LDAP server descriptor
diff --git a/src/nsdbc/Makefile.am b/src/nsdbc/Makefile.am
index e64d259..b1da7ad 100644
--- a/src/nsdbc/Makefile.am
+++ b/src/nsdbc/Makefile.am
@@ -25,7 +25,7 @@ 
 
 sbin_PROGRAMS		= nsdb-annotate nsdb-describe nsdb-list \
 			  nsdb-update-nci nsdb-remove-nci nsdb-nces \
-			  nsdb-delete-nsdb \
+			  nsdb-delete-nsdb nsdb-simple-nce \
 			  nsdb-create-fsn nsdb-delete-fsn nsdb-resolve-fsn \
 			  nsdb-create-fsl nsdb-delete-fsl nsdb-update-fsl
 LDADD			= $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
diff --git a/src/nsdbc/nsdb-simple-nce.c b/src/nsdbc/nsdb-simple-nce.c
new file mode 100644
index 0000000..aa8c8fe
--- /dev/null
+++ b/src/nsdbc/nsdb-simple-nce.c
@@ -0,0 +1,249 @@ 
+/**
+ * @file src/nsdbc/nsdb-simple-nce.c
+ * @brief Create a simple NSDB container entry on a target NSDB server
+ */
+
+/*
+ * Copyright 2012 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#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 <langinfo.h>
+
+#include "fedfs.h"
+#include "nsdb.h"
+#include "xlog.h"
+#include "gpl-boiler.h"
+
+/**
+ * Short form command line options
+ */
+static const char nsdb_simple_nce_opts[] = "?dD:l:qr:";
+
+/**
+ * Long form command line options
+ */
+static const struct option nsdb_simple_nce_longopts[] = {
+	{ "binddn", 1, NULL, 'D', },
+	{ "debug", 0, NULL, 'd', },
+	{ "help", 0, NULL, '?', },
+	{ "nsdbname", 1, NULL, 'l', },
+	{ "nsdbport", 1, NULL, 'r', },
+	{ NULL, 0, NULL, 0, },
+};
+
+/**
+ * Display program synopsis
+ *
+ * @param progname NUL-terminated C string containing name of program
+ */
+static void
+nsdb_simple_nce_usage(const char *progname)
+{
+	fprintf(stderr, "\n%s version " VERSION "\n", progname);
+	fprintf(stderr, "Usage: %s [ -d ] [ -D binddn ] "
+			"[ -l nsdbname ] [ -r nsdbport ] "
+			"parent-dn\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         Bind DN\n");
+	fprintf(stderr, "\t-l, --nsdbname       NSDB hostname\n");
+	fprintf(stderr, "\t-r, --nsdbport       NSDB port\n");
+
+	fprintf(stderr, "%s", fedfs_gpl_boilerplate);
+
+	exit((int)FEDFS_ERR_INVAL);
+}
+
+/**
+ * Program entry point
+ *
+ * @param argc count of command line arguments
+ * @param argv array of NUL-terminated C strings containing command line arguments
+ * @return program exit status
+ */
+int
+main(int argc, char **argv)
+{
+	char *progname, *binddn, *nsdbname, *nce, *parent;
+	unsigned short nsdbport;
+	unsigned int ldap_err;
+	FedFsStatus retval;
+	nsdb_t host;
+	int arg;
+
+	(void)umask(S_IRWXO);
+
+	/* Ensure UTF-8 strings can be handled transparently */
+	if (setlocale(LC_CTYPE, "") == NULL ||
+	    strcmp(nl_langinfo(CODESET), "UTF-8") != 0) {
+		fprintf(stderr, "Failed to set locale and langinfo\n");
+		exit((int)FEDFS_ERR_INVAL);
+	}
+
+	/* Set the basename */
+	if ((progname = strrchr(argv[0], '/')) != NULL)
+		progname++;
+	else
+		progname = argv[0];
+
+	/* For the libraries */
+	xlog_stderr(1);
+	xlog_syslog(0);
+	xlog_open(progname);
+
+	nsdb_env(&nsdbname, &nsdbport, &binddn, NULL);
+
+	while ((arg = getopt_long(argc, argv, nsdb_simple_nce_opts,
+			nsdb_simple_nce_longopts, NULL)) != -1) {
+		switch (arg) {
+		case 'd':
+			xlog_config(D_ALL, 1);
+			break;
+		case 'D':
+			binddn = optarg;
+			break;
+		case 'l':
+			nsdbname = optarg;
+			break;
+		case 'r':
+			if (!nsdb_parse_port_string(optarg, &nsdbport)) {
+				fprintf(stderr, "Bad port number: %s\n",
+					optarg);
+				nsdb_simple_nce_usage(progname);
+			}
+			break;
+		default:
+			fprintf(stderr, "Invalid command line "
+				"argument: %c\n", (char)arg);
+		case '?':
+			nsdb_simple_nce_usage(progname);
+		}
+	}
+	if (argc == optind + 1) {
+		parent = argv[optind];
+	} else if (argc > optind + 1) {
+		fprintf(stderr, "Unrecognized positional parameters\n");
+		nsdb_simple_nce_usage(progname);
+	} else {
+		fprintf(stderr, "No parent DN specified\n");
+		nsdb_simple_nce_usage(progname);
+	}
+	if (nsdbname == NULL) {
+		fprintf(stderr, "Missing required command line argument\n");
+		nsdb_simple_nce_usage(progname);
+	}
+
+	retval = nsdb_lookup_nsdb(nsdbname, nsdbport, &host, NULL);
+	switch (retval) {
+	case FEDFS_OK:
+		break;
+	case FEDFS_ERR_NSDB_PARAMS:
+		fprintf(stderr, "No connection parameters for NSDB %s:%u\n",
+			nsdbname, nsdbport);
+		goto out;
+	default:
+		fprintf(stderr, "Failed to look up NSDB %s:%u: %s\n",
+			nsdbname, nsdbport,
+			nsdb_display_fedfsstatus(retval));
+		goto out;
+	}
+
+	if (binddn == NULL)
+		binddn = (char *)nsdb_default_binddn(host);
+	retval = nsdb_open_nsdb(host, binddn, NULL, &ldap_err);
+	switch (retval) {
+	case FEDFS_OK:
+		break;
+	case FEDFS_ERR_NSDB_CONN:
+		fprintf(stderr, "Failed to connect to NSDB %s:%u\n",
+			nsdbname, nsdbport);
+		goto out_free;
+	case FEDFS_ERR_NSDB_AUTH:
+		fprintf(stderr, "Failed to authenticate to NSDB %s:%u\n",
+			nsdbname, nsdbport);
+		goto out_free;
+	case FEDFS_ERR_NSDB_LDAP_VAL:
+		fprintf(stderr, "Failed to authenticate to NSDB %s:%u: %s\n",
+			nsdbname, nsdbport, ldap_err2string(ldap_err));
+		goto out_free;
+	default:
+		fprintf(stderr, "Failed to bind to NSDB %s:%u: %s\n",
+			nsdbname, nsdbport,
+			nsdb_display_fedfsstatus(retval));
+		goto out_free;
+	}
+
+	retval = nsdb_create_simple_nce_s(host, parent, &nce, &ldap_err);
+	switch (retval) {
+	case FEDFS_OK:
+		break;
+	case FEDFS_ERR_NSDB_LDAP_VAL:
+		fprintf(stderr, "Failed to create NCE: %s\n",
+			ldap_err2string(ldap_err));
+		goto out_close;
+	default:
+		fprintf(stderr, "Failed to create NCE: %s\n",
+			nsdb_display_fedfsstatus(retval));
+		goto out_close;
+	}
+
+	retval = nsdb_update_nci_s(host, nce, &ldap_err);
+	switch (retval) {
+	case FEDFS_OK:
+		printf("Successfully created NCE %s\n", nce);
+		break;
+	case FEDFS_ERR_NSDB_NONCE:
+		/* XXX: should not occur */
+		fprintf(stderr, "Entry %s is not a naming context "
+			"for this NSDB\n", nce);
+		break;
+	case FEDFS_ERR_NSDB_LDAP_VAL:
+		fprintf(stderr, "Failed to update NCI: %s\n",
+			ldap_err2string(ldap_err));
+		break;
+	default:
+		fprintf(stderr, "Failed to update NCI: %s\n",
+			nsdb_display_fedfsstatus(retval));
+	}
+	free(nce);
+
+out_close:
+	nsdb_close_nsdb(host);
+
+out_free:
+	nsdb_free_nsdb(host);
+
+out:
+	exit((int)retval);
+}