diff --git a/contribs/pam/Makefile.am b/contribs/pam/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..d7dcf1fb1fb5a3929c0911253a02bee7b3501c2f
--- /dev/null
+++ b/contribs/pam/Makefile.am
@@ -0,0 +1,45 @@
+#
+# Makefile for SLURM PAM library
+#
+
+AUTOMAKE_OPTIONS = foreign
+
+CFLAGS = -fPIC
+INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/..
+LIBS =
+# Without "-module", we get error: `pam_slurm.la' is not a standard libtool library name
+PLUGIN_FLAGS = -nostdlib -shared -module
+
+if HAVE_PAM
+pam_lib = pam_slurm.la
+else
+pam_lib =
+endif
+
+pkglib_LTLIBRARIES = $(pam_lib)
+
+if HAVE_PAM
+pam_slurm_la_SOURCES = 				\
+	pam_slurm.c				\
+	$(top_builddir)/src/common/hostlist.c	\
+	$(top_builddir)/src/common/hostlist.h
+pam_slurm_la_LDFLAGS = $(PLUGIN_FLAGS)
+else
+EXTRA_pam_slurm_la_SOURCES = pam_slurm.c
+endif
+
+# Compiles fine
+# Link line bad:
+# gcc -shared  .libs/pam_slurm.o .libs/hostlist.o      -Wl,-soname -Wl,pam_slurm.so.0 -o .libs/pam_slurm.so.0.0.0
+#.libs/pam_slurm.o: In function `_init':
+# pam_slurm.c:(.text+0xa33): multiple definition of `_init'
+# /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crti.o:/build/buildd/glibc-2.9/build-tree/amd64-libc/csu/crti.S:25: first defined here
+# .libs/pam_slurm.o: In function `_fini':
+# pam_slurm.c:(.text+0xa87): multiple definition of `_fini'
+# /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crti.o:/build/buildd/glibc-2.9/build-tree/amd64-libc/csu/crti.S:37: first defined here
+# collect2: ld returned 1 exit status
+
+# Simple link is fine
+# gcc -shared -nostdlib .libs/pam_slurm.o .libs/hostlist.o      -Wl,-soname -Wl,pam_slurm.so.0 -o .libs/pam_slurm.so.0.0.0
+
+
diff --git a/contribs/pam/Makefile.in b/contribs/pam/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..3f3bd95a67241f179cf441b95277fb69915554c5
--- /dev/null
+++ b/contribs/pam/Makefile.in
@@ -0,0 +1,615 @@
+# Makefile.in generated by automake 1.10.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# Makefile for SLURM PAM library
+#
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = contribs/pam
+DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/auxdir/acx_pthread.m4 \
+	$(top_srcdir)/auxdir/libtool.m4 \
+	$(top_srcdir)/auxdir/ltoptions.m4 \
+	$(top_srcdir)/auxdir/ltsugar.m4 \
+	$(top_srcdir)/auxdir/ltversion.m4 \
+	$(top_srcdir)/auxdir/lt~obsolete.m4 \
+	$(top_srcdir)/auxdir/slurm.m4 \
+	$(top_srcdir)/auxdir/x_ac__system_configuration.m4 \
+	$(top_srcdir)/auxdir/x_ac_affinity.m4 \
+	$(top_srcdir)/auxdir/x_ac_aix.m4 \
+	$(top_srcdir)/auxdir/x_ac_blcr.m4 \
+	$(top_srcdir)/auxdir/x_ac_bluegene.m4 \
+	$(top_srcdir)/auxdir/x_ac_cflags.m4 \
+	$(top_srcdir)/auxdir/x_ac_cray.m4 \
+	$(top_srcdir)/auxdir/x_ac_databases.m4 \
+	$(top_srcdir)/auxdir/x_ac_debug.m4 \
+	$(top_srcdir)/auxdir/x_ac_elan.m4 \
+	$(top_srcdir)/auxdir/x_ac_env.m4 \
+	$(top_srcdir)/auxdir/x_ac_federation.m4 \
+	$(top_srcdir)/auxdir/x_ac_gpl_licensed.m4 \
+	$(top_srcdir)/auxdir/x_ac_gtk.m4 \
+	$(top_srcdir)/auxdir/x_ac_iso.m4 \
+	$(top_srcdir)/auxdir/x_ac_munge.m4 \
+	$(top_srcdir)/auxdir/x_ac_ncurses.m4 \
+	$(top_srcdir)/auxdir/x_ac_pam.m4 \
+	$(top_srcdir)/auxdir/x_ac_printf_null.m4 \
+	$(top_srcdir)/auxdir/x_ac_ptrace.m4 \
+	$(top_srcdir)/auxdir/x_ac_readline.m4 \
+	$(top_srcdir)/auxdir/x_ac_setpgrp.m4 \
+	$(top_srcdir)/auxdir/x_ac_setproctitle.m4 \
+	$(top_srcdir)/auxdir/x_ac_sgi_job.m4 \
+	$(top_srcdir)/auxdir/x_ac_slurm_ssl.m4 \
+	$(top_srcdir)/auxdir/x_ac_sun_const.m4 \
+	$(top_srcdir)/auxdir/x_ac_xcpu.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h $(top_builddir)/slurm/slurm.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(pkglibdir)"
+pkglibLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(pkglib_LTLIBRARIES)
+pam_slurm_la_LIBADD =
+am__pam_slurm_la_SOURCES_DIST = pam_slurm.c \
+	$(top_builddir)/src/common/hostlist.c \
+	$(top_builddir)/src/common/hostlist.h
+@HAVE_PAM_TRUE@am_pam_slurm_la_OBJECTS = pam_slurm.lo hostlist.lo
+am__EXTRA_pam_slurm_la_SOURCES_DIST = pam_slurm.c
+pam_slurm_la_OBJECTS = $(am_pam_slurm_la_OBJECTS)
+pam_slurm_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(pam_slurm_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_PAM_TRUE@am_pam_slurm_la_rpath = -rpath $(pkglibdir)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/slurm
+depcomp = $(SHELL) $(top_srcdir)/auxdir/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(pam_slurm_la_SOURCES) $(EXTRA_pam_slurm_la_SOURCES)
+DIST_SOURCES = $(am__pam_slurm_la_SOURCES_DIST) \
+	$(am__EXTRA_pam_slurm_la_SOURCES_DIST)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTHD_CFLAGS = @AUTHD_CFLAGS@
+AUTHD_LIBS = @AUTHD_LIBS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BG_INCLUDES = @BG_INCLUDES@
+BLCR_CPPFLAGS = @BLCR_CPPFLAGS@
+BLCR_HOME = @BLCR_HOME@
+BLCR_LDFLAGS = @BLCR_LDFLAGS@
+BLCR_LIBS = @BLCR_LIBS@
+BLUEGENE_LOADED = @BLUEGENE_LOADED@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = -fPIC
+CMD_LDFLAGS = @CMD_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ELAN_LIBS = @ELAN_LIBS@
+EXEEXT = @EXEEXT@
+FEDERATION_LDFLAGS = @FEDERATION_LDFLAGS@
+FGREP = @FGREP@
+GREP = @GREP@
+GTK2_CFLAGS = @GTK2_CFLAGS@
+GTK2_LIBS = @GTK2_LIBS@
+HAVEMYSQLCONFIG = @HAVEMYSQLCONFIG@
+HAVEPGCONFIG = @HAVEPGCONFIG@
+HAVEPKGCONFIG = @HAVEPKGCONFIG@
+HAVE_AIX = @HAVE_AIX@
+HAVE_ELAN = @HAVE_ELAN@
+HAVE_FEDERATION = @HAVE_FEDERATION@
+HAVE_OPENSSL = @HAVE_OPENSSL@
+HAVE_SOME_CURSES = @HAVE_SOME_CURSES@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = 
+LIBTOOL = @LIBTOOL@
+LIB_LDFLAGS = @LIB_LDFLAGS@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MUNGE_CPPFLAGS = @MUNGE_CPPFLAGS@
+MUNGE_LDFLAGS = @MUNGE_LDFLAGS@
+MUNGE_LIBS = @MUNGE_LIBS@
+MYSQL_CFLAGS = @MYSQL_CFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NCURSES = @NCURSES@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NUMA_LIBS = @NUMA_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PGSQL_CFLAGS = @PGSQL_CFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PLPA_LIBS = @PLPA_LIBS@
+PROCTRACKDIR = @PROCTRACKDIR@
+PROJECT = @PROJECT@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+RELEASE = @RELEASE@
+SED = @SED@
+SEMAPHORE_LIBS = @SEMAPHORE_LIBS@
+SEMAPHORE_SOURCES = @SEMAPHORE_SOURCES@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SLURMCTLD_PORT = @SLURMCTLD_PORT@
+SLURMDBD_PORT = @SLURMDBD_PORT@
+SLURMD_PORT = @SLURMD_PORT@
+SLURM_API_AGE = @SLURM_API_AGE@
+SLURM_API_CURRENT = @SLURM_API_CURRENT@
+SLURM_API_MAJOR = @SLURM_API_MAJOR@
+SLURM_API_REVISION = @SLURM_API_REVISION@
+SLURM_API_VERSION = @SLURM_API_VERSION@
+SLURM_MAJOR = @SLURM_MAJOR@
+SLURM_MICRO = @SLURM_MICRO@
+SLURM_MINOR = @SLURM_MINOR@
+SLURM_PREFIX = @SLURM_PREFIX@
+SLURM_VERSION = @SLURM_VERSION@
+SO_LDFLAGS = @SO_LDFLAGS@
+SSL_CPPFLAGS = @SSL_CPPFLAGS@
+SSL_LDFLAGS = @SSL_LDFLAGS@
+SSL_LIBS = @SSL_LIBS@
+STRIP = @STRIP@
+UTIL_LIBS = @UTIL_LIBS@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AUTOMAKE_OPTIONS = foreign
+INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/..
+# Without "-module", we get error: `pam_slurm.la' is not a standard libtool library name
+PLUGIN_FLAGS = -nostdlib -shared -module
+@HAVE_PAM_FALSE@pam_lib = 
+@HAVE_PAM_TRUE@pam_lib = pam_slurm.la
+pkglib_LTLIBRARIES = $(pam_lib)
+@HAVE_PAM_TRUE@pam_slurm_la_SOURCES = \
+@HAVE_PAM_TRUE@	pam_slurm.c				\
+@HAVE_PAM_TRUE@	$(top_builddir)/src/common/hostlist.c	\
+@HAVE_PAM_TRUE@	$(top_builddir)/src/common/hostlist.h
+
+@HAVE_PAM_TRUE@pam_slurm_la_LDFLAGS = $(PLUGIN_FLAGS)
+@HAVE_PAM_FALSE@EXTRA_pam_slurm_la_SOURCES = pam_slurm.c
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  contribs/pam/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  contribs/pam/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	test -z "$(pkglibdir)" || $(MKDIR_P) "$(DESTDIR)$(pkglibdir)"
+	@list='$(pkglib_LTLIBRARIES)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    f=$(am__strip_dir) \
+	    echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(pkglibLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(pkglibdir)/$$f'"; \
+	    $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(pkglibLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(pkglibdir)/$$f"; \
+	  else :; fi; \
+	done
+
+uninstall-pkglibLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(pkglib_LTLIBRARIES)'; for p in $$list; do \
+	  p=$(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$p'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$p"; \
+	done
+
+clean-pkglibLTLIBRARIES:
+	-test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES)
+	@list='$(pkglib_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+pam_slurm.la: $(pam_slurm_la_OBJECTS) $(pam_slurm_la_DEPENDENCIES) 
+	$(pam_slurm_la_LINK) $(am_pam_slurm_la_rpath) $(pam_slurm_la_OBJECTS) $(pam_slurm_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostlist.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_slurm.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+hostlist.lo: $(top_builddir)/src/common/hostlist.c
+@am__fastdepCC_TRUE@	$(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hostlist.lo -MD -MP -MF $(DEPDIR)/hostlist.Tpo -c -o hostlist.lo `test -f '$(top_builddir)/src/common/hostlist.c' || echo '$(srcdir)/'`$(top_builddir)/src/common/hostlist.c
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/hostlist.Tpo $(DEPDIR)/hostlist.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_builddir)/src/common/hostlist.c' object='hostlist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hostlist.lo `test -f '$(top_builddir)/src/common/hostlist.c' || echo '$(srcdir)/'`$(top_builddir)/src/common/hostlist.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(pkglibdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-pkglibLTLIBRARIES
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pkglibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-pkglibLTLIBRARIES ctags distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-pkglibLTLIBRARIES \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags uninstall uninstall-am uninstall-pkglibLTLIBRARIES
+
+
+# Compiles fine
+# Link line bad:
+# gcc -shared  .libs/pam_slurm.o .libs/hostlist.o      -Wl,-soname -Wl,pam_slurm.so.0 -o .libs/pam_slurm.so.0.0.0
+#.libs/pam_slurm.o: In function `_init':
+# pam_slurm.c:(.text+0xa33): multiple definition of `_init'
+# /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crti.o:/build/buildd/glibc-2.9/build-tree/amd64-libc/csu/crti.S:25: first defined here
+# .libs/pam_slurm.o: In function `_fini':
+# pam_slurm.c:(.text+0xa87): multiple definition of `_fini'
+# /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crti.o:/build/buildd/glibc-2.9/build-tree/amd64-libc/csu/crti.S:37: first defined here
+# collect2: ld returned 1 exit status
+
+# Simple link is fine
+# gcc -shared -nostdlib .libs/pam_slurm.o .libs/hostlist.o      -Wl,-soname -Wl,pam_slurm.so.0 -o .libs/pam_slurm.so.0.0.0
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/contribs/pam/README b/contribs/pam/README
new file mode 100644
index 0000000000000000000000000000000000000000..1d4f6a452e8d6f100d078ae4ce06f05d97cabeda
--- /dev/null
+++ b/contribs/pam/README
@@ -0,0 +1,62 @@
+Module Name:
+  pam_slurm
+
+Authors:
+  Chris Dunlap <cdunlap@llnl.gov>
+  Jim Garlick  <garlick@llnl.gov>
+  Moe Jette    <jette1@llnl.gov>
+
+Management Groups Provided:
+  account
+
+System Dependencies:
+  libslurm.so
+
+Overview:
+  Restricts access to compute nodes in a cluster using SLURM.
+
+Recognized Arguments:
+  debug; no_warn; rsh_kludge; rlogin_kludge
+
+Description:
+  This module restricts access to compute nodes in a cluster where Simple 
+  Linux Utility for Resource Managment (SLURM) is in use.  Access is granted
+  to root, any user with an SLURM-launched job currently running on the node,
+  or any user who has allocated resources on the node according to the SLURM
+  database.
+
+  The behavior of this module can be modified with the following flags:
+
+    debug         - log debugging information to the system log file
+    no_warn       - suppress warning messages to the application
+    rsh_kludge    - prevent truncation of first char from rsh error msg
+    rlogin_kludge - prevent "staircase-effect" following rlogin error msg
+
+Examples / Suggested Usage:
+  Use of this module is recommended on any compute node where you want to
+  limit access to just those users who are currently scheduled to run jobs.
+
+  For /etc/pam.d/ style configurations where modules live in /lib/security/,
+  add the following line to the PAM configuration file for the appropriate
+  service(s) (eg, /etc/pam.d/system-auth):
+
+    account    required     /lib/security/pam_slurm.so
+
+  If you always want to allow access for an administrative group (eg, wheel),
+  stack the pam_access module ahead of pam_slurm:
+
+    account    sufficient   /lib/security/pam_access.so
+    account    required     /lib/security/pam_slurm.so
+
+  Then edit the pam_access configuration file (/etc/security/access.conf):
+
+    +:wheel:ALL
+    -:ALL:ALL
+
+  When access is denied because the user does not have an active job running
+  on the node, an error message is returned to the application:
+
+    Access denied: user foo (uid=1313) has no active jobs.
+
+  This message can be suppressed by specifying the "no_warn" argument in the
+  PAM configuration file.
diff --git a/contribs/pam/hostlist.c b/contribs/pam/hostlist.c
new file mode 100644
index 0000000000000000000000000000000000000000..00698567c1bc7f2117e15c0dbe0495de840731a8
--- /dev/null
+++ b/contribs/pam/hostlist.c
@@ -0,0 +1,2678 @@
+/*****************************************************************************\
+ *  $Id$
+ *****************************************************************************
+ *  $LSDId: hostlist.c 2955 2006-07-27 14:48:33Z grondo $
+ *****************************************************************************
+ *  Copyright (C) 2002 The Regents of the University of California.
+ *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
+ *  Written by Mark Grondona <mgrondona@llnl.gov>
+ *  UCRL-CODE-2002-040.
+ *  
+ *  This file is part of SLURM, a resource management program.
+ *  For details, see <http://www.llnl.gov/linux/slurm/>.
+ *  
+ *  SLURM 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 of the License, or (at your option)
+ *  any later version.
+ *  
+ *  SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
+ *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ *  details.
+ *  
+ *  You should have received a copy of the GNU General Public License along
+ *  with SLURM; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+\*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#  if HAVE_STRING_H
+#    include <string.h>
+#  endif
+#  if HAVE_PTHREAD_H
+#    include <pthread.h>
+#  endif
+#else                /* !HAVE_CONFIG_H */
+#  include <string.h>
+#  include <pthread.h>
+#endif                /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+#include "hostlist.h"
+
+/*
+ * lsd_fatal_error : fatal error macro
+ */
+#ifdef WITH_LSD_FATAL_ERROR_FUNC
+#  undef lsd_fatal_error
+   extern void lsd_fatal_error(char *file, int line, char *mesg);
+#else /* !WITH_LSD_FATAL_ERROR_FUNC */
+#  ifndef lsd_fatal_error
+#    define lsd_fatal_error(file, line, mesg)                                \
+       do {                                                                  \
+           fprintf(stderr, "ERROR: [%s:%d] %s: %s\n",                        \
+           file, line, mesg, strerror(errno));                               \
+       } while (0)
+#  endif /* !lsd_fatal_error */
+#endif /* !WITH_LSD_FATAL_ERROR_FUNC */
+
+/*
+ * lsd_nonmem_error
+ */
+#ifdef WITH_LSD_NOMEM_ERROR_FUNC
+#  undef lsd_nomem_error
+   extern void * lsd_nomem_error(char *file, int line, char *mesg);
+#else /* !WITH_LSD_NOMEM_ERROR_FUNC */
+#  ifndef lsd_nomem_error
+#    define lsd_nomem_error(file, line, mesg) (NULL)
+#  endif /* !lsd_nomem_error */
+#endif /* !WITH_LSD_NOMEM_ERROR_FUNC */
+
+/*
+ * OOM helper function
+ *  Automatically call lsd_nomem_error with appropriate args
+ *  and set errno to ENOMEM
+ */
+#define out_of_memory(mesg)                                                  \
+    do {                                                                     \
+        errno = ENOMEM;                                                      \
+        return(lsd_nomem_error(__FILE__, __LINE__, mesg));                   \
+    } while (0)
+
+/* 
+ * Some constants and tunables:
+ */
+
+/* number of elements to allocate when extending the hostlist array */
+#define HOSTLIST_CHUNK    16
+
+/* max host range: anything larger will be assumed to be an error */
+#define MAX_RANGE    16384    /* 16K Hosts */
+
+/* max host suffix value */
+#define MAX_HOST_SUFFIX 1<<25
+
+/* max number of ranges that will be processed between brackets */
+#define MAX_RANGES    10240    /* 10K Ranges */
+
+/* size of internal hostname buffer (+ some slop), hostnames will probably
+ * be truncated if longer than MAXHOSTNAMELEN */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN    64
+#endif
+
+/* max size of internal hostrange buffer */
+#define MAXHOSTRANGELEN 1024
+
+/* ----[ Internal Data Structures ]---- */
+
+/* hostname type: A convenience structure used in parsing single hostnames */
+struct hostname_components {
+    char *hostname;         /* cache of initialized hostname        */
+    char *prefix;           /* hostname prefix                      */
+    unsigned long num;      /* numeric suffix                       */
+
+    /* string representation of numeric suffix
+     * points into `hostname'                                       */
+    char *suffix;
+};
+
+typedef struct hostname_components *hostname_t;
+
+/* hostrange type: A single prefix with `hi' and `lo' numeric suffix values */
+struct hostrange_components {
+    char *prefix;        /* alphanumeric prefix: */
+
+    /* beginning (lo) and end (hi) of suffix range */
+    unsigned long lo, hi;
+
+    /* width of numeric output format
+     * (pad with zeros up to this width) */
+    int width;
+
+    /* If singlehost is 1, `lo' and `hi' are invalid */
+    unsigned singlehost:1;
+};
+
+typedef struct hostrange_components *hostrange_t;
+
+/* The hostlist type: An array based list of hostrange_t's */
+struct hostlist {
+#ifndef NDEBUG
+#define HOSTLIST_MAGIC    57005
+    int magic;
+#endif
+#if    WITH_PTHREADS
+    pthread_mutex_t mutex;
+#endif                /* WITH_PTHREADS */
+
+    /* current number of elements available in array */
+    int size;
+
+    /* current number of ranges stored in array */
+    int nranges;
+
+    /* current number of hosts stored in hostlist */
+    int nhosts;
+
+    /* pointer to hostrange array */
+    hostrange_t *hr;
+
+    /* list of iterators */
+    struct hostlist_iterator *ilist;
+
+};
+
+
+/* a hostset is a wrapper around a hostlist */
+struct hostset {
+    hostlist_t hl;
+};
+
+struct hostlist_iterator {
+#ifndef NDEBUG
+    int magic;
+#endif
+    /* hostlist we are traversing */
+    hostlist_t hl;
+
+    /* current index of iterator in hl->hr[] */
+    int idx;
+
+    /* current hostrange object in list hl, i.e. hl->hr[idx] */
+    hostrange_t hr;
+
+    /* current depth we've traversed into range hr */
+    int depth;
+
+    /* next ptr for lists of iterators */
+    struct hostlist_iterator *next;
+};
+
+
+/* ---- ---- */
+
+/* ------[ static function prototypes ]------ */
+
+static void _error(char *file, int line, char *mesg, ...);
+static char * _next_tok(char *, char **);
+static int    _zero_padded(unsigned long, int);
+static int    _width_equiv(unsigned long, int *, unsigned long, int *);
+
+static size_t        host_prefix_end(const char *);
+static hostname_t    hostname_create(const char *);
+static void          hostname_destroy(hostname_t);
+static int           hostname_suffix_is_valid(hostname_t);
+static int           hostname_suffix_width(hostname_t);
+
+static hostrange_t   hostrange_new(void);
+static hostrange_t   hostrange_create_single(const char *);
+static hostrange_t   hostrange_create(char *, unsigned long, unsigned long, int);
+static unsigned long hostrange_count(hostrange_t);
+static hostrange_t   hostrange_copy(hostrange_t);
+static void          hostrange_destroy(hostrange_t);
+static hostrange_t   hostrange_delete_host(hostrange_t, unsigned long);
+static int           hostrange_cmp(hostrange_t, hostrange_t);
+static int           hostrange_prefix_cmp(hostrange_t, hostrange_t);
+static int           hostrange_within_range(hostrange_t, hostrange_t);
+static int           hostrange_width_combine(hostrange_t, hostrange_t);
+static int           hostrange_empty(hostrange_t);
+static char *        hostrange_pop(hostrange_t);
+static char *        hostrange_shift(hostrange_t);
+static int           hostrange_join(hostrange_t, hostrange_t);
+static hostrange_t   hostrange_intersect(hostrange_t, hostrange_t);
+static int           hostrange_hn_within(hostrange_t, hostname_t);
+static size_t        hostrange_to_string(hostrange_t hr, size_t, char *, char *);
+static size_t        hostrange_numstr(hostrange_t, size_t, char *);
+
+static hostlist_t  hostlist_new(void);
+static hostlist_t _hostlist_create_bracketed(const char *, char *, char *);
+static int         hostlist_resize(hostlist_t, size_t);
+static int         hostlist_expand(hostlist_t);
+static int         hostlist_push_range(hostlist_t, hostrange_t);
+static int         hostlist_push_hr(hostlist_t, char *, unsigned long,
+                                    unsigned long, int);
+static int         hostlist_insert_range(hostlist_t, hostrange_t, int);
+static void        hostlist_delete_range(hostlist_t, int n);
+static void        hostlist_coalesce(hostlist_t hl);
+static void        hostlist_collapse(hostlist_t hl);
+static hostlist_t _hostlist_create(const char *, char *, char *);
+static void        hostlist_shift_iterators(hostlist_t, int, int, int);
+static int        _attempt_range_join(hostlist_t, int);
+static int        _is_bracket_needed(hostlist_t, int);
+
+static hostlist_iterator_t hostlist_iterator_new(void);
+static void               _iterator_advance(hostlist_iterator_t);
+static void               _iterator_advance_range(hostlist_iterator_t);
+
+static int hostset_find_host(hostset_t, const char *);
+
+/* ------[ macros ]------ */
+
+#ifdef WITH_PTHREADS
+#  define mutex_init(mutex)                                                  \
+     do {                                                                    \
+        int e = pthread_mutex_init(mutex, NULL);                             \
+        if (e) {                                                             \
+            errno = e;                                                       \
+            lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex init:");     \
+            abort();                                                         \
+        }                                                                    \
+     } while (0)
+
+#  define mutex_lock(mutex)                                                  \
+     do {                                                                    \
+        int e = pthread_mutex_lock(mutex);                                   \
+        if (e) {                                                             \
+           errno = e;                                                        \
+           lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex lock:");      \
+           abort();                                                          \
+        }                                                                    \
+     } while (0)
+
+#  define mutex_unlock(mutex)                                                \
+     do {                                                                    \
+        int e = pthread_mutex_unlock(mutex);                                 \
+        if (e) {                                                             \
+            errno = e;                                                       \
+            lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex unlock:");   \
+            abort();                                                         \
+        }                                                                    \
+     } while (0)
+
+#  define mutex_destroy(mutex)                                               \
+     do {                                                                    \
+        int e = pthread_mutex_destroy(mutex);                                \
+        if (e) {                                                             \
+            errno = e;                                                       \
+            lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex destroy:");  \
+            abort();                                                         \
+        }                                                                    \
+     } while (0)
+
+#else                /* !WITH_PTHREADS */
+
+#  define mutex_init(mutex)
+#  define mutex_lock(mutex)
+#  define mutex_unlock(mutex)
+#  define mutex_destroy(mutex)
+
+#endif                /* WITH_PTHREADS */
+
+#define LOCK_HOSTLIST(_hl)                                                   \
+      do {                                                                   \
+          assert(_hl != NULL);                                               \
+          mutex_lock(&(_hl)->mutex);                                         \
+          assert((_hl)->magic == HOSTLIST_MAGIC);                            \
+      } while (0)
+
+#define UNLOCK_HOSTLIST(_hl)                                                 \
+      do {                                                                   \
+          mutex_unlock(&(_hl)->mutex);                                       \
+      } while (0)                       
+
+#define seterrno_ret(_errno, _rc)                                            \
+      do {                                                                   \
+          errno = _errno;                                                    \
+          return _rc;                                                        \
+      } while (0)
+
+/* ------[ Function Definitions ]------ */
+
+/* ----[ general utility functions ]---- */
+
+
+/*
+ *  Varargs capable error reporting via lsd_fatal_error()
+ */
+static void _error(char *file, int line, char *msg, ...)
+{
+    va_list ap;
+    char    buf[1024];
+    int     len = 0;
+    va_start(ap, msg);
+
+    len = vsnprintf(buf, 1024, msg, ap);
+    if ((len < 0) || (len > 1024)) 
+        buf[1023] = '\0';
+
+    lsd_fatal_error(file, line, buf);
+
+    va_end(ap);
+    return;
+}
+
+static int _advance_past_brackets (char *tok, char **str)
+{
+    /* if _single_ opening bracket exists b/w tok and str, push str
+     * past first closing bracket to next seperator */
+    if (   memchr(tok, '[', *str - tok) != NULL
+        && memchr(tok, ']', *str - tok) == NULL ) {
+        char *q = strchr(*str, ']');
+        if (q && memchr(*str, '[', q - *str) == NULL) {
+            *str = q + 1;
+            return (1);
+        }
+    }
+
+    return 0;
+}
+
+/* 
+ * Helper function for host list string parsing routines 
+ * Returns a pointer to the next token; additionally advance *str
+ * to the next separator.
+ *
+ * next_tok was taken directly from pdsh courtesy of Jim Garlick.
+ * (with modifications to support bracketed hostlists, i.e.:
+ *  xxx[xx,xx,xx] is a single token)
+ *
+ */
+static char * _next_tok(char *sep, char **str)
+{
+    char *tok;
+
+    /* push str past any leading separators */
+    while (**str != '\0' && strchr(sep, **str) != '\0')
+        (*str)++;
+
+    if (**str == '\0')
+        return NULL;
+
+    /* assign token ptr */
+    tok = *str;
+
+    /*
+     * Advance str past any separators, but if a separator occurs between
+     *  brackets, e.g. foo[0-3,5], then advance str past closing brackets and
+     *  try again.
+     */
+    do {
+        /* push str past token and leave pointing to first separator */
+        while (**str != '\0' && strchr(sep, **str) == '\0')
+            (*str)++;
+    } while (_advance_past_brackets (tok, str));
+
+   /* nullify consecutive separators and push str beyond them */
+    while (**str != '\0' && strchr(sep, **str) != '\0')
+        *(*str)++ = '\0';
+
+    return tok;
+}
+
+
+/* return the number of zeros needed to pad "num" to "width"
+ */
+static int _zero_padded(unsigned long num, int width)
+{
+    int n = 1;
+    while (num /= 10L)
+        n++;
+    return width > n ? width - n : 0;
+}
+
+/* test whether two format `width' parameters are "equivalent"
+ * The width arguments "wn" and "wm" for integers "n" and "m" 
+ * are equivalent if:
+ *  
+ *  o  wn == wm  OR
+ *
+ *  o  applying the same format width (either wn or wm) to both of  
+ *     'n' and 'm' will not change the zero padding of *either* 'm' nor 'n'.
+ *
+ *  If this function returns 1 (or true), the appropriate width value
+ *  (either 'wm' or 'wn') will have been adjusted such that both format
+ *  widths are equivalent.
+ */
+static int _width_equiv(unsigned long n, int *wn, unsigned long m, int *wm)
+{
+    int npad, nmpad, mpad, mnpad;
+
+    if (wn == wm)
+        return 1;
+
+    npad = _zero_padded(n, *wn);
+    nmpad = _zero_padded(n, *wm);
+    mpad = _zero_padded(m, *wm);
+    mnpad = _zero_padded(m, *wn);
+
+    if (npad != nmpad && mpad != mnpad)
+        return 0;
+
+    if (npad != nmpad) {
+        if (mpad == mnpad) {
+            *wm = *wn;
+            return 1;
+        } else
+            return 0;
+    } else {        /* mpad != mnpad */
+        if (npad == nmpad) {
+            *wn = *wm;
+            return 1;
+        } else
+            return 0;
+    }
+
+    /* not reached */
+}
+
+
+/* ----[ hostname_t functions ]---- */
+
+/* 
+ * return the location of the last char in the hostname prefix
+ */
+static size_t host_prefix_end(const char *hostname)
+{
+    size_t idx = strlen(hostname) - 1;
+
+    while (idx >= 0 && isdigit((char) hostname[idx])) 
+        idx--;
+    return idx;
+}
+
+/* 
+ * create a hostname_t object from a string hostname
+ */
+static hostname_t hostname_create(const char *hostname)
+{
+    hostname_t hn = NULL;
+    char *p = '\0';
+    size_t idx = 0;
+
+    assert(hostname != NULL);
+
+    if (!(hn = (hostname_t) malloc(sizeof(*hn))))
+        out_of_memory("hostname create");
+
+    idx = host_prefix_end(hostname);
+
+    if (!(hn->hostname = strdup(hostname))) {
+        free(hn);
+        out_of_memory("hostname create");
+    }
+
+    hn->num = 0;
+    hn->prefix = NULL;
+    hn->suffix = NULL;
+
+    if (idx == strlen(hostname) - 1) {
+        if ((hn->prefix = strdup(hostname)) == NULL) {
+            hostname_destroy(hn);
+            out_of_memory("hostname prefix create");
+        }
+        return hn;
+    }
+
+    hn->suffix = hn->hostname + idx + 1;
+    hn->num = strtoul(hn->suffix, &p, 10);
+
+    if ((*p == '\0') && (hn->num <= MAX_HOST_SUFFIX)) {
+        if (!(hn->prefix = malloc((idx + 2) * sizeof(char)))) {
+            hostname_destroy(hn);
+            out_of_memory("hostname prefix create");
+        }
+        memcpy(hn->prefix, hostname, idx + 1);
+        hn->prefix[idx + 1] = '\0';
+    } else {
+        if (!(hn->prefix = strdup(hostname))) {
+            hostname_destroy(hn);
+            out_of_memory("hostname prefix create");
+        }
+        hn->suffix = NULL;
+    }
+
+    return hn;
+}
+
+/* free a hostname object
+ */
+static void hostname_destroy(hostname_t hn)
+{
+    if (hn == NULL)
+        return;
+    hn->suffix = NULL;
+    if (hn->hostname)
+        free(hn->hostname);
+    if (hn->prefix)
+        free(hn->prefix);
+    free(hn);
+}
+
+/* return true if the hostname has a valid numeric suffix 
+ */
+static int hostname_suffix_is_valid(hostname_t hn)
+{
+    return hn->suffix != NULL;
+}
+
+/* return the width (in characters) of the numeric part of the hostname
+ */
+static int hostname_suffix_width(hostname_t hn)
+{
+    assert(hn->suffix != NULL);
+    return (int) strlen(hn->suffix);
+}
+
+
+/* ----[ hostrange_t functions ]---- */
+
+/* allocate a new hostrange object 
+ */
+static hostrange_t hostrange_new(void)
+{
+    hostrange_t new = (hostrange_t) malloc(sizeof(*new));
+    if (!new) 
+        out_of_memory("hostrange create");
+    return new;
+}
+
+/* Create a hostrange_t containing a single host without a valid suffix
+ * hr->prefix will represent the entire hostname.
+ */
+static hostrange_t hostrange_create_single(const char *prefix)
+{
+    hostrange_t new;
+
+    assert(prefix != NULL);
+
+    if ((new = hostrange_new()) == NULL)
+        goto error1;
+
+    if ((new->prefix = strdup(prefix)) == NULL)
+        goto error2;
+
+    new->singlehost = 1;
+    new->lo = 0L;
+    new->hi = 0L;
+    new->width = 0;
+
+    return new;
+
+  error2:
+    free(new);
+  error1:
+    out_of_memory("hostrange create single");
+}
+
+
+/* Create a hostrange object with a prefix, hi, lo, and format width
+ */
+static hostrange_t
+hostrange_create(char *prefix, unsigned long lo, unsigned long hi, int width)
+{
+    hostrange_t new;
+
+    assert(prefix != NULL);
+
+    if ((new = hostrange_new()) == NULL)
+        goto error1;
+
+    if ((new->prefix = strdup(prefix)) == NULL)
+        goto error2;
+
+    new->lo = lo;
+    new->hi = hi;
+    new->width = width;
+
+    new->singlehost = 0;
+
+    return new;
+
+  error2:
+    free(new);
+  error1:
+    out_of_memory("hostrange create");
+}
+
+
+/* Return the number of hosts stored in the hostrange object
+ */
+static unsigned long hostrange_count(hostrange_t hr)
+{
+    assert(hr != NULL);
+    if (hr->singlehost)
+        return 1;
+    else
+        return hr->hi - hr->lo + 1;
+}
+
+/* Copy a hostrange object
+ */
+static hostrange_t hostrange_copy(hostrange_t hr)
+{
+    assert(hr != NULL);
+
+    if (hr->singlehost)
+        return hostrange_create_single(hr->prefix);
+    else
+        return hostrange_create(hr->prefix, hr->lo, hr->hi,
+                    hr->width);
+}
+
+
+/* free memory allocated by the hostrange object
+ */
+static void hostrange_destroy(hostrange_t hr)
+{
+    if (hr == NULL)
+        return;
+    if (hr->prefix)
+        free(hr->prefix);
+    free(hr);
+}
+
+/* hostrange_delete_host() deletes a specific host from the range.
+ * If the range is split into two, the greater range is returned,
+ * and `hi' of the lesser range is adjusted accordingly. If the
+ * highest or lowest host is deleted from a range, NULL is returned
+ * and the hostrange hr is adjusted properly.
+ */
+static hostrange_t hostrange_delete_host(hostrange_t hr, unsigned long n)
+{
+    hostrange_t new = NULL;
+
+    assert(hr != NULL);
+    assert(n >= hr->lo && n <= hr->hi);
+
+    if (n == hr->lo)
+        hr->lo++;
+    else if (n == hr->hi)
+        hr->hi--;
+    else {
+        if (!(new = hostrange_copy(hr)))
+            out_of_memory("hostrange copy");
+        hr->hi = n - 1;
+        new->lo = n + 1;
+    }
+
+    return new;
+}
+
+/* hostrange_cmp() is used to sort hostrange objects. It will
+ * sort based on the following (in order):
+ *  o result of strcmp on prefixes
+ *  o if widths are compatible, then: 
+ *       sort based on lowest suffix in range
+ *    else
+ *       sort based on width                     */
+static int hostrange_cmp(hostrange_t h1, hostrange_t h2)
+{
+    int retval;
+
+    assert(h1 != NULL);
+    assert(h2 != NULL);
+
+    if ((retval = hostrange_prefix_cmp(h1, h2)) == 0)
+        retval = hostrange_width_combine(h1, h2) ?
+            h1->lo - h2->lo : h1->width - h2->width;
+
+    return retval;
+}
+
+
+/* compare the prefixes of two hostrange objects. 
+ * returns:
+ *    < 0   if h1 prefix is less than h2 OR h1 == NULL.
+ *
+ *      0   if h1's prefix and h2's prefix match, 
+ *          UNLESS, either h1 or h2 (NOT both) do not have a valid suffix.
+ *
+ *    > 0   if h1's prefix is greater than h2's OR h2 == NULL. */
+static int hostrange_prefix_cmp(hostrange_t h1, hostrange_t h2)
+{
+    int retval;
+    if (h1 == NULL)
+        return 1;
+    if (h2 == NULL)
+        return -1;
+
+    retval = strcmp(h1->prefix, h2->prefix);
+    return retval == 0 ? h2->singlehost - h1->singlehost : retval;
+}
+
+/* returns true if h1 and h2 would be included in the same bracketed hostlist.
+ * h1 and h2 will be in the same bracketed list iff:
+ *
+ *  1. h1 and h2 have same prefix
+ *  2. neither h1 nor h2 are singlet hosts (i.e. invalid suffix)
+ *
+ *  (XXX: Should incompatible widths be placed in the same bracketed list?
+ *        There's no good reason not to, except maybe aesthetics)
+ */
+static int hostrange_within_range(hostrange_t h1, hostrange_t h2)
+{
+    if (hostrange_prefix_cmp(h1, h2) == 0)
+        return h1->singlehost || h2->singlehost ? 0 : 1;
+    else
+        return 0;
+}
+
+
+/* compare two hostrange objects to determine if they are width 
+ * compatible,  returns:
+ *  1 if widths can safely be combined
+ *  0 if widths cannot be safely combined
+ */
+static int hostrange_width_combine(hostrange_t h0, hostrange_t h1)
+{
+    assert(h0 != NULL);
+    assert(h1 != NULL);
+
+    return _width_equiv(h0->lo, &h0->width, h1->lo, &h1->width);
+}
+
+
+/* Return true if hostrange hr contains no hosts, i.e. hi < lo
+ */
+static int hostrange_empty(hostrange_t hr)
+{
+    assert(hr != NULL);
+    return ((hr->hi < hr->lo) || (hr->hi == (unsigned long) -1));
+}
+
+/* return the string representation of the last host in hostrange hr
+ * and remove that host from the range (i.e. decrement hi if possible)
+ *
+ * Returns NULL if malloc fails OR there are no more hosts left
+ */
+static char *hostrange_pop(hostrange_t hr)
+{
+    size_t size = 0;
+    char *host = NULL;
+
+    assert(hr != NULL);
+
+    if (hr->singlehost) {
+        hr->lo++;    /* effectively set count == 0 */
+        host = strdup(hr->prefix);
+    } else if (hostrange_count(hr) > 0) {
+        size = strlen(hr->prefix) + hr->width + 16;    
+        if (!(host = (char *) malloc(size * sizeof(char))))
+            out_of_memory("hostrange pop");
+        snprintf(host, size, "%s%0*lu", hr->prefix, 
+             hr->width, hr->hi--);
+    }
+
+    return host;
+}
+
+/* Same as hostrange_pop(), but remove host from start of range */
+static char *hostrange_shift(hostrange_t hr)
+{
+    size_t size = 0;
+    char *host = NULL;
+
+    assert(hr != NULL);
+
+    if (hr->singlehost) {
+        hr->lo++;
+        if (!(host = strdup(hr->prefix)))
+            out_of_memory("hostrange shift");
+    } else if (hostrange_count(hr) > 0) {
+        size = strlen(hr->prefix) + hr->width + 16;
+        if (!(host = (char *) malloc(size * sizeof(char))))
+            out_of_memory("hostrange shift");
+        snprintf(host, size, "%s%0*lu", hr->prefix,
+             hr->width, hr->lo++);
+    }
+
+    return host;
+}
+
+
+/* join two hostrange objects.
+ *
+ * returns:
+ *
+ * -1 if ranges do not overlap (including incompatible zero padding)
+ *  0 if ranges join perfectly
+ * >0 number of hosts that were duplicated in  h1 and h2 
+ *
+ * h2 will be coalesced into h1 if rc >= 0
+ *
+ * it is assumed that h1->lo <= h2->lo, i.e. hr1 <= hr2
+ *
+ */
+static int hostrange_join(hostrange_t h1, hostrange_t h2)
+{
+    int duplicated = -1;
+
+    assert(h1 != NULL);
+    assert(h2 != NULL);
+    assert(hostrange_cmp(h1, h2) <= 0);
+
+    if (hostrange_prefix_cmp(h1, h2) == 0 &&
+        hostrange_width_combine(h1, h2)) {
+
+        if (h1->singlehost && h2->singlehost) {    /* matching singlets  */
+            duplicated = 1;
+        } else if (h1->hi == h2->lo - 1) {    /* perfect join       */
+            h1->hi = h2->hi;
+            duplicated = 0;
+        } else if (h1->hi >= h2->lo) {    /* some duplication   */
+            if (h1->hi < h2->hi) {
+                duplicated = h1->hi - h2->lo + 1;
+                h1->hi = h2->hi;
+            } else
+                duplicated = hostrange_count(h2);
+        }
+    }
+
+    return duplicated;
+}
+
+/* hostrange intersect returns the intersection (common hosts)
+ * of hostrange objects h1 and h2. If there is no intersection,
+ * NULL is returned.
+ *
+ * It is assumed that h1 <= h2 (i.e. h1->lo <= h2->lo)
+ */
+static hostrange_t hostrange_intersect(hostrange_t h1, hostrange_t h2)
+{
+    hostrange_t new = NULL;
+
+    assert(h1 != NULL);
+    assert(h2 != NULL);
+
+    if (h1->singlehost || h2->singlehost)
+        return NULL;
+
+    assert(hostrange_cmp(h1, h2) <= 0);
+
+    if ((hostrange_prefix_cmp(h1, h2) == 0)
+        && (h1->hi > h2->lo) 
+        && (hostrange_width_combine(h1, h2))) {
+
+        if (!(new = hostrange_copy(h1)))
+            return NULL;
+        new->lo = h2->lo;
+        new->hi = h2->hi < h1->hi ? h2->hi : h1->hi;
+    }
+
+    return new;
+}
+
+/* return 1 if hostname hn is within the hostrange hr
+ *        0 if not.
+ */
+static int hostrange_hn_within(hostrange_t hr, hostname_t hn)
+{
+    int retval = 0;
+
+    if (hr->singlehost && (strcmp(hn->hostname, hr->prefix) == 0))
+        return 1;
+
+    if (strcmp(hr->prefix, hn->prefix) == 0) {
+        if (!hostname_suffix_is_valid(hn)) {
+            if (hr->singlehost)
+                retval = 1;
+        } else if (hn->num <= hr->hi && hn->num >= hr->lo) {
+            int width = hostname_suffix_width(hn);
+            int num = hn->num;
+            retval = _width_equiv(hr->lo, &hr->width, num, &width);
+        }
+    }
+    return retval;
+}
+
+
+/* copy a string representation of the hostrange hr into buffer buf,
+ * writing at most n chars including NUL termination
+ */
+static size_t
+hostrange_to_string(hostrange_t hr, size_t n, char *buf, char *separator)
+{
+    unsigned long i;
+    int truncated = 0;
+    int len = 0;
+    char sep = separator == NULL ? ',' : separator[0];
+
+    if (n == 0)
+        return 0;
+
+    if (hr->singlehost)
+        return snprintf(buf, n, "%s", hr->prefix);
+
+    for (i = hr->lo; i <= hr->hi; i++) {
+        size_t m = (n - len) <= n ? n - len : 0; /* check for < 0 */
+        int ret = snprintf(buf + len, m, "%s%0*lu",
+                   hr->prefix, hr->width, i);
+        if (ret < 0 || ret >= m) {
+            len = n;
+            truncated = 1;
+            break;
+        }
+        len+=ret;
+        buf[len++] = sep;
+    }
+
+    if (truncated) {
+        buf[n-1] = '\0';
+        return -1;
+    } else {
+        /* back up over final separator */
+        buf[--len] = '\0';
+        return len;
+    }
+}
+
+/* Place the string representation of the numeric part of hostrange into buf
+ * writing at most n chars including NUL termination.
+ */
+static size_t hostrange_numstr(hostrange_t hr, size_t n, char *buf)
+{
+    int len = 0;
+
+    assert(buf != NULL);
+
+    if (hr->singlehost || n == 0)
+        return 0;
+
+    len = snprintf(buf, n, "%0*lu", hr->width, hr->lo);
+
+    if ((len >= 0) && (len < n) && (hr->lo < hr->hi)) {
+        int len2 = snprintf(buf+len, n-len, "-%0*lu", hr->width, hr->hi);
+        if (len2 < 0) 
+            len = -1;
+        else
+            len += len2;
+    }
+
+    return len;
+}
+
+
+/* ----[ hostlist functions ]---- */
+
+/* Create a new hostlist object. 
+ * Returns an empty hostlist, or NULL if memory allocation fails.
+ */
+static hostlist_t hostlist_new(void)
+{
+    int i;
+    hostlist_t new = (hostlist_t) malloc(sizeof(*new));
+    if (!new)
+        goto fail1;
+
+    assert(new->magic = HOSTLIST_MAGIC);
+    mutex_init(&new->mutex);
+
+    new->hr = (hostrange_t *) malloc(HOSTLIST_CHUNK * sizeof(hostrange_t));
+    if (!new->hr)
+        goto fail2;
+
+    /* set entries in hostrange array to NULL */
+    for (i = 0; i < HOSTLIST_CHUNK; i++)
+        new->hr[i] = NULL;
+
+    new->size = HOSTLIST_CHUNK;
+    new->nranges = 0;
+    new->nhosts = 0;
+    new->ilist = NULL;
+    return new;
+
+  fail2:
+    free(new);
+  fail1:
+    out_of_memory("hostlist_create");
+}
+
+
+/* Resize the internal array used to store the list of hostrange objects.
+ *
+ * returns 1 for a successful resize,
+ *         0 if call to _realloc fails    
+ *
+ * It is assumed that the caller has the hostlist hl locked 
+ */
+static int hostlist_resize(hostlist_t hl, size_t newsize)
+{
+    int i;
+    size_t oldsize;
+    assert(hl != NULL);
+    assert(hl->magic == HOSTLIST_MAGIC);
+    oldsize = hl->size;
+    hl->size = newsize;
+    hl->hr = realloc((void *) hl->hr, hl->size*sizeof(hostrange_t));
+    if (!(hl->hr)) 
+        return 0;
+
+    for (i = oldsize; i < newsize; i++)
+        hl->hr[i] = NULL;
+
+    return 1;
+}
+
+/* Resize hostlist by one HOSTLIST_CHUNK
+ * Assumes that hostlist hl is locked by caller
+ */
+static int hostlist_expand(hostlist_t hl)
+{
+    if (!hostlist_resize(hl, hl->size + HOSTLIST_CHUNK))
+        return 0;
+    else
+        return 1;
+}
+
+/* Push a hostrange object onto hostlist hl
+ * Returns the number of hosts successfully pushed onto hl
+ * or -1 if there was an error allocating memory
+ */
+static int hostlist_push_range(hostlist_t hl, hostrange_t hr)
+{
+    hostrange_t tail;
+    int retval;
+
+    assert(hr != NULL);
+    LOCK_HOSTLIST(hl);
+
+    tail = (hl->nranges > 0) ? hl->hr[hl->nranges-1] : hl->hr[0];
+
+    if (hl->size == hl->nranges && !hostlist_expand(hl))
+        goto error;
+
+    if (hl->nranges > 0
+        && hostrange_prefix_cmp(tail, hr) == 0
+        && tail->hi == hr->lo - 1
+        && hostrange_width_combine(tail, hr)) {
+        tail->hi = hr->hi;
+    } else {
+        if ((hl->hr[hl->nranges++] = hostrange_copy(hr)) == NULL)
+            goto error;
+    }
+
+    retval = hl->nhosts += hostrange_count(hr);
+
+    UNLOCK_HOSTLIST(hl);
+
+    return retval;
+
+  error:
+    UNLOCK_HOSTLIST(hl);
+    return -1;
+}
+
+
+
+/* Same as hostlist_push_range() above, but prefix, lo, hi, and width
+ * are passed as args 
+ */
+static int
+hostlist_push_hr(hostlist_t hl, char *prefix, unsigned long lo,
+         unsigned long hi, int width)
+{
+    hostrange_t hr = hostrange_create(prefix, lo, hi, width);
+    int retval = hostlist_push_range(hl, hr);
+    hostrange_destroy(hr);
+    return retval;
+}
+
+/* Insert a range object hr into position n of the hostlist hl
+ * Assumes that hl->mutex is already held by calling process
+ */
+static int hostlist_insert_range(hostlist_t hl, hostrange_t hr, int n)
+{
+    int i;
+    hostrange_t tmp;
+    hostlist_iterator_t hli;
+
+    assert(hl != NULL);
+    assert(hl->magic == HOSTLIST_MAGIC);
+    assert(hr != NULL);
+
+    if (n > hl->nranges)
+        return 0;
+
+    if (hl->size == hl->nranges && !hostlist_expand(hl))
+        return 0;
+
+    /* copy new hostrange into slot "n" in array */
+    tmp = hl->hr[n];
+    hl->hr[n] = hostrange_copy(hr);
+
+    /* push remaining hostrange entries up */
+    for (i = n + 1; i < hl->nranges + 1; i++) {
+        hostrange_t last = hl->hr[i];
+        hl->hr[i] = tmp;
+        tmp = last;
+    }
+    hl->nranges++;
+
+    /* adjust hostlist iterators if needed */
+    for (hli = hl->ilist; hli; hli = hli->next) {
+        if (hli->idx >= n)
+            hli->hr = hli->hl->hr[++hli->idx];
+    }
+
+    return 1;
+}
+
+/* Delete the range at position n in the range array
+ * Assumes the hostlist lock is already held.
+ */
+static void hostlist_delete_range(hostlist_t hl, int n)
+{
+    int i;
+    hostrange_t old;
+
+    assert(hl != NULL);
+    assert(hl->magic == HOSTLIST_MAGIC);
+    assert(n < hl->nranges && n >= 0);
+
+    old = hl->hr[n];
+    for (i = n; i < hl->nranges - 1; i++)
+        hl->hr[i] = hl->hr[i + 1];
+    hl->nranges--;
+    hl->hr[hl->nranges] = NULL;
+    hostlist_shift_iterators(hl, n, 0, 1);
+
+    /* XXX caller responsible for adjusting nhosts */
+    /* hl->nhosts -= hostrange_count(old) */
+
+    hostrange_destroy(old);
+}
+
+#if WANT_RECKLESS_HOSTRANGE_EXPANSION
+
+/* The reckless hostrange expansion function.
+ * See comment in hostlist.h:hostlist_create() for more info on
+ * the different choices for hostlist notation.
+ */
+hostlist_t _hostlist_create(const char *hostlist, char *sep, char *r_op)
+{
+    char *str, *orig;
+    char *tok, *cur;
+    int high, low, fmt = 0;
+    char prefix[256] = "";
+    int pos = 0;
+    int error = 0;
+    char range_op = r_op[0];/* XXX support > 1 char range ops in future? */
+
+    hostlist_t new = hostlist_new();
+
+    orig = str = strdup(hostlist);
+
+    /* return an empty list if an empty string was passed in */
+    if (str == NULL || strlen(str) == 0)
+        goto done;
+
+    /* Use hostlist_create_bracketed if we see "[" */
+    if (strchr(str, '[') != NULL)
+        return _hostlist_create_bracketed(hostlist, sep, r_op);
+
+    while ((tok = _next_tok(sep, &str)) != NULL) {
+
+        /* save the current string for error messages */
+        cur = tok;
+
+        high = low = 0;
+
+        /* find end of alpha part 
+         *   do this by finding last occurence of range_op in str */
+        pos = strlen(tok) - 1;
+        if (strstr(tok, r_op) != '\0') {
+            while (pos >= 0 && (char) tok[pos] != range_op) 
+                pos--;
+        }
+
+        /* now back up past any digits */
+        while (pos >= 0 && isdigit((char) tok[--pos])) {;}
+
+        /* Check for valid x-y range (x must be a digit) 
+         *   Reset pos if the range is not valid         */
+        if (!isdigit((char) tok[++pos]))
+            pos = strlen(tok) - 1;
+
+        /* create prefix string 
+         * if prefix will be zero length, but prefix already exists
+         * use the previous prefix and fmt
+         */
+        if ((pos > 0) || (prefix[0] == '\0')) {
+            memcpy(prefix, tok, (size_t) pos * sizeof(char));
+            prefix[pos] = '\0';
+
+            /* push pointer past prefix */
+            tok += pos;
+
+            /* count number of digits for ouput fmt */
+            for (fmt = 0; isdigit(tok[fmt]); ++fmt) {;}
+
+            if (fmt == 0)
+                error = 1;
+
+        } else
+            tok += pos;
+
+        /* get lower bound */
+        low = strtoul(tok, (char **) &tok, 10);
+
+        if (*tok == range_op) {    /* now get range upper bound */
+            /* push pointer past range op */
+            ++tok;
+
+            /* find length of alpha part */
+            for (pos = 0; tok[pos] && !isdigit(tok[pos]); ++pos) {;}
+
+            /* alpha part must match prefix or error
+             * this could mean we've got something like "rtr1-a2"
+             * so just record an error
+             */
+            if (pos > 0) {
+                if (pos != strlen(prefix) ||
+                    strncmp(prefix, tok, pos) != 0)
+                    error = 1;
+            }
+
+            if (*tok != '\0')
+                tok += pos;
+
+            /* make sure we have digits to the end */
+            for (pos = 0; tok[pos] && isdigit((char) tok[pos]); ++pos) {;}
+
+            if (pos > 0) {    /* we have digits to process */
+                high = strtoul(tok, (char **) &tok, 10);
+            } else {    /* bad boy, no digits */
+                error = 1;
+            }
+
+            if ((low > high) || (high - low > MAX_RANGE))
+                error = 1;
+
+        } else {    /* single value */
+            high = 0;    /* special case, ugh. */
+        }
+
+        /* error if: 
+         * 1. we are not at end of string
+         * 2. upper bound equals lower bound
+         */
+        if (*tok != '\0' || high == low)
+            error = 1;
+
+        if (error) {    /* assume this is not a range on any error */
+            hostlist_push_host(new, cur);
+        } else {
+            if (high < low)
+                high = low;
+            hostlist_push_hr(new, prefix, low, high, fmt);
+        }
+
+        error = 0;
+    }
+
+  done:
+    free(orig);
+
+    return new;
+}
+
+#else                /* !WANT_RECKLESS_HOSTRANGE_EXPANSION */
+
+hostlist_t _hostlist_create(const char *hostlist, char *sep, char *r_op) 
+{
+    return _hostlist_create_bracketed(hostlist, sep, r_op);
+}
+
+#endif                /* WANT_RECKLESS_HOSTRANGE_EXPANSION */
+
+struct _range {
+    unsigned long lo, hi;
+    int width;
+};
+
+/* Grab a single range from str 
+ * returns 1 if str contained a valid number or range,
+ *         0 if conversion of str to a range failed.
+ */
+static int _parse_single_range(const char *str, struct _range *range)
+{
+    char *p, *q;
+    char *orig = strdup(str);
+    if (!orig) 
+        seterrno_ret(ENOMEM, 0);
+
+    if ((p = strchr(str, '-'))) {
+        *p++ = '\0';
+        if (*p == '-')     /* do NOT allow negative numbers */
+            goto error;
+    }
+    range->lo = strtoul(str, &q, 10);
+    if (q == str) 
+        goto error;
+
+    range->hi = (p && *p) ? strtoul(p, &q, 10) : range->lo;
+
+    if (q == p || *q != '\0') 
+        goto error;
+
+    if (range->lo > range->hi) 
+        goto error;
+
+    if (range->hi - range->lo + 1 > MAX_RANGE ) {
+        _error(__FILE__, __LINE__, "Too many hosts in range `%s'", orig);
+        free(orig);
+        seterrno_ret(ERANGE, 0);
+    }
+
+    free(orig);
+    range->width = strlen(str);
+    return 1;
+
+  error:
+    _error(__FILE__, __LINE__, "Invalid range: `%s'", orig);
+    free(orig);
+    seterrno_ret(EINVAL, 0);
+}
+
+
+/*
+ * Convert 'str' containing comma separated digits and ranges into an array
+ *  of struct _range types (max 'len' elements).  
+ *
+ * Return number of ranges created, or -1 on error.
+ */
+static int _parse_range_list(char *str, struct _range *ranges, int len)
+{
+    char *p;
+    int count = 0;
+
+    while (str) {
+        if (count == len)
+            return -1;
+        if ((p = strchr(str, ',')))
+            *p++ = '\0';
+        if (!_parse_single_range(str, &ranges[count++])) 
+            return -1;  
+        str = p;
+    }
+    return count;
+}
+
+static void
+_push_range_list(hostlist_t hl, char *pfx, struct _range *rng,
+             int n)
+{
+    int i;
+    for (i = 0; i < n; i++) {
+        hostlist_push_hr(hl, pfx, rng->lo, rng->hi, rng->width);
+        rng++;
+    }
+}
+
+static void
+_push_range_list_with_suffix(hostlist_t hl, char *pfx, char *sfx, 
+                             struct _range *rng, int n)
+{
+    int i;
+    unsigned long j;
+    for (i = 0; i < n; i++) {
+        for (j = rng->lo; j <= rng->hi; j++) {
+            char host[4096];
+            hostrange_t hr;
+            snprintf (host, 4096, "%s%0*lu%s", pfx, rng->width, j, sfx);
+            hr = hostrange_create_single (host);
+            hostlist_push_range (hl, hr);
+            /*
+             * hr is copied in hostlist_push_range. Need to free here.
+             */
+            hostrange_destroy (hr);
+        }
+        rng++;
+    }
+}
+
+/*
+ * Create a hostlist from a string with brackets '[' ']' to aid 
+ * detection of ranges and compressed lists
+ */
+static hostlist_t 
+_hostlist_create_bracketed(const char *hostlist, char *sep, char *r_op)
+{
+    hostlist_t new = hostlist_new();
+    struct _range ranges[MAX_RANGES];
+    int nr, err;
+    char *p, *tok, *str, *orig;
+    char cur_tok[1024];
+
+    if (hostlist == NULL)
+        return new;
+
+    if (!(orig = str = strdup(hostlist))) {
+        hostlist_destroy(new);
+        return NULL;
+    }
+
+    while ((tok = _next_tok(sep, &str)) != NULL) {
+        strncpy(cur_tok, tok, 1024);
+
+        if ((p = strchr(tok, '[')) != NULL) {
+            char *q, *prefix = tok;
+            *p++ = '\0';
+
+            if ((q = strchr(p, ']'))) {
+                *q = '\0';
+                nr = _parse_range_list(p, ranges, MAX_RANGES);
+                if (nr < 0) 
+                    goto error;
+
+                if (*(++q) != '\0')
+                    _push_range_list_with_suffix (new, prefix, q, ranges, nr);
+                else
+                    _push_range_list(new, prefix, ranges, nr);
+
+                
+            } else
+                hostlist_push_host(new, cur_tok);
+
+        } else
+            hostlist_push_host(new, cur_tok);
+    }
+
+    free(orig);
+    return new;
+
+  error:
+    err = errno;
+    hostlist_destroy(new);
+    free(orig);
+    seterrno_ret(err, NULL);
+}
+
+
+
+hostlist_t hostlist_create(const char *str)
+{
+    return _hostlist_create(str, "\t, ", "-");
+}
+
+
+hostlist_t hostlist_copy(const hostlist_t hl)
+{
+    int i;
+    hostlist_t new;
+
+    if (hl == NULL)
+        return NULL;
+
+    LOCK_HOSTLIST(hl);
+    if (!(new = hostlist_new()))
+        goto done;
+
+    new->nranges = hl->nranges;
+    new->nhosts = hl->nhosts;
+    if (new->nranges > new->size)
+        hostlist_resize(new, new->nranges);
+
+    for (i = 0; i < hl->nranges; i++)
+        new->hr[i] = hostrange_copy(hl->hr[i]);
+
+  done:
+    UNLOCK_HOSTLIST(hl);
+    return new;
+}
+
+
+void hostlist_destroy(hostlist_t hl)
+{
+    int i;
+    if (hl == NULL)
+        return;
+    LOCK_HOSTLIST(hl);
+    while (hl->ilist) {
+        mutex_unlock(&hl->mutex);
+        hostlist_iterator_destroy(hl->ilist);
+        mutex_lock(&hl->mutex);
+    }
+    for (i = 0; i < hl->nranges; i++)
+        hostrange_destroy(hl->hr[i]);
+    free(hl->hr);
+    assert(hl->magic = 0x1);
+    UNLOCK_HOSTLIST(hl);
+    mutex_destroy(&hl->mutex);
+    free(hl);
+}
+
+
+int hostlist_push(hostlist_t hl, const char *hosts)
+{
+    hostlist_t new;
+    int retval;
+    if (hosts == NULL)
+        return 0;
+    new = hostlist_create(hosts);
+    if (!new)
+        return 0;
+    mutex_lock(&new->mutex);
+    retval = new->nhosts;
+    mutex_unlock(&new->mutex);
+    hostlist_push_list(hl, new);
+    hostlist_destroy(new);
+    return retval;
+}
+
+int hostlist_push_host(hostlist_t hl, const char *str)
+{
+    hostrange_t hr;
+    hostname_t hn;
+
+    if (str == NULL)
+        return 0;
+
+    hn = hostname_create(str);
+
+    if (hostname_suffix_is_valid(hn)) {
+        hr = hostrange_create(hn->prefix, hn->num, hn->num,
+                      hostname_suffix_width(hn));
+    } else
+        hr = hostrange_create_single(str);
+
+    hostlist_push_range(hl, hr);
+
+    hostrange_destroy(hr);
+    hostname_destroy(hn);
+
+    return 1;
+}
+
+int hostlist_push_list(hostlist_t h1, hostlist_t h2)
+{
+    int i, n = 0;
+
+    if (h2 == NULL)
+        return 0;
+
+    LOCK_HOSTLIST(h2);
+
+    for (i = 0; i < h2->nranges; i++)
+        n += hostlist_push_range(h1, h2->hr[i]);
+
+    UNLOCK_HOSTLIST(h2);
+
+    return n;
+}
+
+
+char *hostlist_pop(hostlist_t hl)
+{
+    char *host = NULL;
+
+    LOCK_HOSTLIST(hl);
+    if (hl->nhosts > 0) {
+        hostrange_t hr = hl->hr[hl->nranges - 1];
+        host = hostrange_pop(hr);
+        hl->nhosts--;
+        if (hostrange_empty(hr)) {
+            hostrange_destroy(hl->hr[--hl->nranges]);
+            hl->hr[hl->nranges] = NULL;
+        }
+    }
+    UNLOCK_HOSTLIST(hl);
+    return host;
+}
+
+/* find all iterators affected by a shift (or deletion) at 
+ * hl->hr[idx], depth, with the deletion of n ranges */
+static void
+hostlist_shift_iterators(hostlist_t hl, int idx, int depth, int n)
+{
+    hostlist_iterator_t i;
+    for (i = hl->ilist; i; i = i->next) {
+        if (n == 0) {
+            if (i->idx == idx && i->depth >= depth)
+                i->depth = i->depth > -1 ? i->depth - 1 : -1;
+        } else {
+            if (i->idx >= idx) {
+                if ((i->idx -= n) >= 0)
+                    i->hr = i->hl->hr[i->idx];
+                else
+                    hostlist_iterator_reset(i);
+            }
+        }
+    }
+}
+
+char *hostlist_shift(hostlist_t hl)
+{
+    char *host = NULL;
+
+    LOCK_HOSTLIST(hl);
+
+    if (hl->nhosts > 0) {
+        hostrange_t hr = hl->hr[0];
+
+        host = hostrange_shift(hr);
+        hl->nhosts--;
+
+        if (hostrange_empty(hr)) {
+            hostlist_delete_range(hl, 0);
+            /* hl->nranges--; */
+        } else
+            hostlist_shift_iterators(hl, 0, 0, 0);
+    }
+
+    UNLOCK_HOSTLIST(hl);
+
+    return host;
+}
+
+
+char *hostlist_pop_range(hostlist_t hl)
+{
+    int i;
+    char buf[MAXHOSTRANGELEN + 1];
+    hostlist_t hltmp;
+    hostrange_t tail;
+
+    LOCK_HOSTLIST(hl);
+    if (hl->nranges < 1 || !(hltmp = hostlist_new())) {
+        UNLOCK_HOSTLIST(hl);
+        return NULL;
+    }
+
+    i = hl->nranges - 2;
+    tail = hl->hr[hl->nranges - 1];
+    while (i >= 0 && hostrange_within_range(tail, hl->hr[i]))
+        i--;
+
+    for (i++; i < hl->nranges; i++) {
+        hostlist_push_range(hltmp, hl->hr[i]);
+        hostrange_destroy(hl->hr[i]);
+        hl->hr[i] = NULL;
+    }
+    hl->nhosts -= hltmp->nhosts;
+    hl->nranges -= hltmp->nranges;
+
+    UNLOCK_HOSTLIST(hl);
+    hostlist_ranged_string(hltmp, MAXHOSTRANGELEN, buf);
+    hostlist_destroy(hltmp);
+    return strdup(buf);
+}
+
+
+char *hostlist_shift_range(hostlist_t hl)
+{
+    int i;
+    char buf[1024];
+    hostlist_t hltmp = hostlist_new();
+    if (!hltmp)
+        return NULL;
+
+    LOCK_HOSTLIST(hl);
+
+    if (hl->nranges == 0) {
+        hostlist_destroy(hltmp);
+        UNLOCK_HOSTLIST(hl);
+        return NULL;
+    }
+
+    i = 0;
+    do {
+        hostlist_push_range(hltmp, hl->hr[i]);
+        hostrange_destroy(hl->hr[i]);
+    } while ( (++i < hl->nranges) 
+            && hostrange_within_range(hltmp->hr[0], hl->hr[i]) );
+
+    hostlist_shift_iterators(hl, i, 0, hltmp->nranges);
+
+    /* shift rest of ranges back in hl */
+    for (; i < hl->nranges; i++) {
+        hl->hr[i - hltmp->nranges] = hl->hr[i];
+        hl->hr[i] = NULL;
+    }
+    hl->nhosts -= hltmp->nhosts;
+    hl->nranges -= hltmp->nranges;
+
+    UNLOCK_HOSTLIST(hl);
+
+    hostlist_ranged_string(hltmp, 1024, buf);
+    hostlist_destroy(hltmp);
+
+    return strdup(buf);
+}
+
+/* XXX: Note: efficiency improvements needed */
+int hostlist_delete(hostlist_t hl, const char *hosts)
+{
+    int n = 0;
+    char *hostname = NULL;
+    hostlist_t hltmp;
+
+    if (!(hltmp = hostlist_create(hosts)))
+        seterrno_ret(EINVAL, 0);
+
+    while ((hostname = hostlist_pop(hltmp)) != NULL) {
+        n += hostlist_delete_host(hl, hostname);
+        free(hostname);
+    }
+    hostlist_destroy(hltmp);
+
+    return n;
+}
+
+
+/* XXX watch out! poor implementation follows! (fix it at some point) */
+int hostlist_delete_host(hostlist_t hl, const char *hostname)
+{
+    int n = hostlist_find(hl, hostname);
+    if (n >= 0)
+        hostlist_delete_nth(hl, n);
+    return n >= 0 ? 1 : 0;
+}
+
+
+static char *
+_hostrange_string(hostrange_t hr, int depth)
+{
+    char buf[MAXHOSTNAMELEN + 16];
+    int  len = snprintf(buf, MAXHOSTNAMELEN + 15, "%s", hr->prefix);
+
+    if (!hr->singlehost)
+        snprintf(buf+len, MAXHOSTNAMELEN+15 - len, "%0*lu", 
+                 hr->width, hr->lo + depth);
+    return strdup(buf);
+}
+
+char * hostlist_nth(hostlist_t hl, int n)
+{
+    char *host = NULL;
+    int   i, count;
+
+    LOCK_HOSTLIST(hl);
+    count = 0;
+    for (i = 0; i < hl->nranges; i++) {
+        int num_in_range = hostrange_count(hl->hr[i]);
+
+        if (n <= (num_in_range - 1 + count)) {
+            host = _hostrange_string(hl->hr[i], n - count);
+            break;
+        } else
+            count += num_in_range;
+    }
+
+    UNLOCK_HOSTLIST(hl);
+
+    return host;
+}
+
+
+int hostlist_delete_nth(hostlist_t hl, int n)
+{
+    int i, count;
+
+    LOCK_HOSTLIST(hl);
+    assert(n >= 0 && n <= hl->nhosts);
+
+    count = 0;
+
+    for (i = 0; i < hl->nranges; i++) {
+        int num_in_range = hostrange_count(hl->hr[i]);
+        hostrange_t hr = hl->hr[i];
+
+        if (n <= (num_in_range - 1 + count)) {
+            unsigned long num = hr->lo + n - count;
+            hostrange_t new;
+
+            if (hr->singlehost) { /* this wasn't a range */
+                hostlist_delete_range(hl, i);
+            } else if ((new = hostrange_delete_host(hr, num))) {
+                hostlist_insert_range(hl, new, i + 1);
+                hostrange_destroy(new);
+            } else if (hostrange_empty(hr))
+                hostlist_delete_range(hl, i);
+
+            goto done;
+        } else
+            count += num_in_range;
+
+    }
+
+  done:
+    UNLOCK_HOSTLIST(hl);
+    hl->nhosts--;
+    return 1;
+}
+
+int hostlist_count(hostlist_t hl)
+{
+    int retval;
+    LOCK_HOSTLIST(hl);
+    retval = hl->nhosts;
+    UNLOCK_HOSTLIST(hl);
+    return retval;
+}
+
+int hostlist_find(hostlist_t hl, const char *hostname)
+{
+    int i, count, ret = -1;
+    hostname_t hn;
+
+    if (!hostname)
+        return -1;
+
+    hn = hostname_create(hostname);
+
+    LOCK_HOSTLIST(hl);
+
+    for (i = 0, count = 0; i < hl->nranges; i++) {
+        if (hostrange_hn_within(hl->hr[i], hn)) {
+            if (hostname_suffix_is_valid(hn) && !hl->hr[i]->singlehost)
+                ret = count + hn->num - hl->hr[i]->lo;
+            else
+                ret = count;
+            goto done;
+        } else
+            count += hostrange_count(hl->hr[i]);
+    }
+
+  done:
+    UNLOCK_HOSTLIST(hl);
+    hostname_destroy(hn);
+    return ret;
+}
+
+/* hostrange compare with void * arguments to allow use with 
+ * libc qsort()
+ */
+int _cmp(const void *hr1, const void *hr2)
+{
+    hostrange_t *h1 = (hostrange_t *) hr1;
+    hostrange_t *h2 = (hostrange_t *) hr2;
+    return hostrange_cmp((hostrange_t) * h1, (hostrange_t) * h2);
+}
+
+
+void hostlist_sort(hostlist_t hl)
+{
+    hostlist_iterator_t i;
+    LOCK_HOSTLIST(hl);
+
+    if (hl->nranges <= 1) {
+        UNLOCK_HOSTLIST(hl);
+        return;
+    }
+
+    qsort(hl->hr, hl->nranges, sizeof(hostrange_t), &_cmp);
+
+    /* reset all iterators */
+    for (i = hl->ilist; i; i = i->next)
+        hostlist_iterator_reset(i);
+
+    UNLOCK_HOSTLIST(hl);
+
+    hostlist_coalesce(hl);
+
+}
+
+
+/* search through hostlist for ranges that can be collapsed 
+ * does =not= delete any hosts
+ */
+static void hostlist_collapse(hostlist_t hl)
+{
+    int i;
+
+    LOCK_HOSTLIST(hl);
+    for (i = hl->nranges - 1; i > 0; i--) {
+        hostrange_t hprev = hl->hr[i - 1];
+        hostrange_t hnext = hl->hr[i];
+
+        if (hostrange_prefix_cmp(hprev, hnext) == 0 &&
+            hprev->hi == hnext->lo - 1 &&
+            hostrange_width_combine(hprev, hnext)) {
+            hprev->hi = hnext->hi;
+            hostlist_delete_range(hl, i);
+        }
+    }
+    UNLOCK_HOSTLIST(hl);
+}
+
+/* search through hostlist (hl) for intersecting ranges 
+ * split up duplicates and coalesce ranges where possible
+ */
+static void hostlist_coalesce(hostlist_t hl)
+{
+    int i, j;
+    hostrange_t new;
+
+    LOCK_HOSTLIST(hl);
+
+    for (i = hl->nranges - 1; i > 0; i--) {
+
+        new = hostrange_intersect(hl->hr[i - 1], hl->hr[i]);
+
+        if (new) {
+            hostrange_t hprev = hl->hr[i - 1];
+            hostrange_t hnext = hl->hr[i];
+            j = i;
+
+            if (new->hi < hprev->hi)
+                hnext->hi = hprev->hi;
+
+            hprev->hi = new->lo;
+            hnext->lo = new->hi;
+
+            if (hostrange_empty(hprev))
+                hostlist_delete_range(hl, i);
+
+            while (new->lo <= new->hi) {
+                hostrange_t hr = hostrange_create( new->prefix,
+                                                   new->lo, new->lo,
+                                                   new->width );
+
+                if (new->lo > hprev->hi)
+                    hostlist_insert_range(hl, hr, j++);
+
+                if (new->lo < hnext->lo)
+                    hostlist_insert_range(hl, hr, j++);
+
+                hostrange_destroy(hr);
+
+                new->lo++;
+            }
+            i = hl->nranges;
+            hostrange_destroy(new);
+        }
+    }
+    UNLOCK_HOSTLIST(hl);
+
+    hostlist_collapse(hl);
+
+}
+
+/* attempt to join ranges at loc and loc-1 in a hostlist  */
+/* delete duplicates, return the number of hosts deleted  */
+/* assumes that the hostlist hl has been locked by caller */
+static int _attempt_range_join(hostlist_t hl, int loc)
+{
+    int ndup;
+    assert(hl != NULL);
+    assert(hl->magic == HOSTLIST_MAGIC);
+    assert(loc > 0);
+    assert(loc < hl->nranges);
+    ndup = hostrange_join(hl->hr[loc - 1], hl->hr[loc]);
+    if (ndup >= 0) {
+        hostlist_delete_range(hl, loc);
+        hl->nhosts -= ndup;
+    }
+    return ndup;
+}
+
+void hostlist_uniq(hostlist_t hl)
+{
+    int i = 1;
+    hostlist_iterator_t hli;
+    LOCK_HOSTLIST(hl);
+    if (hl->nranges <= 1) {
+        UNLOCK_HOSTLIST(hl);
+        return;
+    }
+    qsort(hl->hr, hl->nranges, sizeof(hostrange_t), &_cmp);
+
+    while (i < hl->nranges) {
+        if (_attempt_range_join(hl, i) < 0) /* No range join occurred */
+            i++;
+    }
+
+    /* reset all iterators */
+    for (hli = hl->ilist; hli; hli = hli->next)
+        hostlist_iterator_reset(hli);
+
+    UNLOCK_HOSTLIST(hl);
+}
+
+
+size_t hostlist_deranged_string(hostlist_t hl, size_t n, char *buf)
+{
+    int i;
+    int len = 0;
+    int truncated = 0;
+
+    LOCK_HOSTLIST(hl);
+    for (i = 0; i < hl->nranges; i++) {
+        size_t m = (n - len) <= n ? n - len : 0;
+        int ret = hostrange_to_string(hl->hr[i], m, buf + len, ",");
+        if (ret < 0 || ret > m) {
+            len = n;
+            truncated = 1;
+            break;
+        }
+        len+=ret;
+        buf[len++] = ',';
+    }
+    UNLOCK_HOSTLIST(hl);
+
+    buf[len > 0 ? --len : 0] = '\0';
+    if (len == n)
+        truncated = 1;
+
+    return truncated ? -1 : len;
+}
+
+/* return true if a bracket is needed for the range at i in hostlist hl */
+static int _is_bracket_needed(hostlist_t hl, int i)
+{
+    hostrange_t h1 = hl->hr[i];
+    hostrange_t h2 = i < hl->nranges - 1 ? hl->hr[i + 1] : NULL;
+    return hostrange_count(h1) > 1 || hostrange_within_range(h1, h2);
+}
+
+/* write the next bracketed hostlist, i.e. prefix[n-m,k,...]
+ * into buf, writing at most n chars including the terminating '\0'
+ *
+ * leaves start pointing to one past last range object in bracketed list,
+ * and returns the number of bytes written into buf.
+ *
+ * Assumes hostlist is locked.
+ */
+static int
+_get_bracketed_list(hostlist_t hl, int *start, const size_t n, char *buf)
+{
+    hostrange_t *hr = hl->hr;
+    int i = *start;
+    int m, len = 0;
+    int bracket_needed = _is_bracket_needed(hl, i);
+
+    len = snprintf(buf, n, "%s", hr[i]->prefix);
+
+    if ((len < 0) || (len > n))
+        return n; /* truncated, buffer filled */
+
+    if (bracket_needed && len < n && len >= 0)
+        buf[len++] = '[';
+
+    do {
+        m = (n - len) <= n ? n - len : 0;
+        len += hostrange_numstr(hr[i], m, buf + len);
+        if (len >= n)
+            break;
+        if (bracket_needed) /* Only need commas inside brackets */
+            buf[len++] = ',';
+    } while (++i < hl->nranges && hostrange_within_range(hr[i], hr[i-1]));
+
+    if (bracket_needed && len < n && len > 0) {
+
+        /* Add trailing bracket (change trailing "," from above to "]" */
+        buf[len - 1] = ']';
+
+        /* NUL terminate for safety, but do not add terminator to len */
+        buf[len]   = '\0';
+
+    } else if (len >= n) {
+        if (n > 0)
+            buf[n-1] = '\0';
+
+    } else {
+        /* If len is > 0, NUL terminate (but do not add to len) */
+        buf[len > 0 ? len : 0] = '\0';
+    }
+
+    *start = i;
+    return len;
+}
+
+size_t hostlist_ranged_string(hostlist_t hl, size_t n, char *buf)
+{
+    int i = 0;
+    int len = 0;
+    int truncated = 0;
+
+    LOCK_HOSTLIST(hl);
+    while (i < hl->nranges && len < n) {
+        len += _get_bracketed_list(hl, &i, n - len, buf + len);
+        if ((len > 0) && (len < n) && (i < hl->nranges))
+            buf[len++] = ',';
+    }
+    UNLOCK_HOSTLIST(hl);
+
+    /* NUL terminate */
+    if (len >= n) {
+        truncated = 1;
+        if (n > 0)
+            buf[n-1] = '\0';
+    } else
+        buf[len > 0 ? len : 0] = '\0';
+
+    return truncated ? -1 : len;
+}
+
+/* ----[ hostlist iterator functions ]---- */
+
+static hostlist_iterator_t hostlist_iterator_new(void)
+{
+    hostlist_iterator_t i = (hostlist_iterator_t) malloc(sizeof(*i));
+    if (!i) 
+        return NULL;
+    i->hl = NULL;
+    i->hr = NULL;
+    i->idx = 0;
+    i->depth = -1;
+    i->next = i;
+    assert(i->magic = HOSTLIST_MAGIC);
+    return i;
+}
+
+hostlist_iterator_t hostlist_iterator_create(hostlist_t hl)
+{
+    hostlist_iterator_t i;
+
+    if (!(i = hostlist_iterator_new()))
+        out_of_memory("hostlist_iterator_create");
+
+    LOCK_HOSTLIST(hl);
+    i->hl = hl;
+    i->hr = hl->hr[0];
+    i->next = hl->ilist;
+    hl->ilist = i;
+    UNLOCK_HOSTLIST(hl);
+    return i;
+}
+
+hostlist_iterator_t hostset_iterator_create(hostset_t set)
+{
+    return hostlist_iterator_create(set->hl);
+}
+
+void hostlist_iterator_reset(hostlist_iterator_t i)
+{
+    assert(i != NULL);
+    assert(i->magic == HOSTLIST_MAGIC);
+    i->idx = 0;
+    i->hr = i->hl->hr[0];
+    i->depth = -1;
+    return;
+}
+
+void hostlist_iterator_destroy(hostlist_iterator_t i)
+{
+    hostlist_iterator_t *pi;
+    if (i == NULL)
+        return;
+    assert(i != NULL);
+    assert(i->magic == HOSTLIST_MAGIC);
+    LOCK_HOSTLIST(i->hl);
+    for (pi = &i->hl->ilist; *pi; pi = &(*pi)->next) {
+        assert((*pi)->magic == HOSTLIST_MAGIC);
+        if (*pi == i) {
+            *pi = (*pi)->next;
+            break;
+        }
+    }
+    UNLOCK_HOSTLIST(i->hl);
+    assert(i->magic = 0x1);
+    free(i);
+}
+
+static void _iterator_advance(hostlist_iterator_t i)
+{
+    assert(i != NULL);
+    assert(i->magic == HOSTLIST_MAGIC);
+    if (i->idx > i->hl->nranges - 1)
+        return;
+    if (++(i->depth) > (i->hr->hi - i->hr->lo)) {
+        i->depth = 0;
+        i->hr = i->hl->hr[++i->idx];
+    }
+}
+
+/* advance iterator to end of current range (meaning within "[" "]")
+ * i.e. advance iterator past all range objects that could be represented
+ * in on bracketed hostlist.
+ */
+static void _iterator_advance_range(hostlist_iterator_t i)
+{
+    int nr, j;
+    hostrange_t *hr;
+    assert(i != NULL);
+    assert(i->magic == HOSTLIST_MAGIC);
+
+    nr = i->hl->nranges;
+    hr = i->hl->hr;
+    j = i->idx;
+    if (++i->depth > 0) {
+        while (++j < nr && hostrange_within_range(i->hr, hr[j])) {;}
+        i->idx = j;
+        i->hr = i->hl->hr[i->idx];
+        i->depth = 0;
+    }
+}
+
+char *hostlist_next(hostlist_iterator_t i)
+{
+    char *buf = NULL;
+    char suffix[16];
+    int len = 0;
+    assert(i != NULL);
+    assert(i->magic == HOSTLIST_MAGIC);
+    LOCK_HOSTLIST(i->hl);
+    _iterator_advance(i);
+
+    if (i->idx > i->hl->nranges - 1) {
+        UNLOCK_HOSTLIST(i->hl);
+        return NULL;
+    }
+
+    suffix[0] = '\0';
+
+    if (!i->hr->singlehost)
+        snprintf (suffix, 15, "%0*lu", i->hr->width, i->hr->lo + i->depth);
+
+    len = strlen (i->hr->prefix) + strlen (suffix) + 1;
+    if (!(buf = malloc (len)))
+        out_of_memory("hostlist_next");
+    
+    buf[0] = '\0';
+    strcat (buf, i->hr->prefix);
+    strcat (buf, suffix);
+
+    UNLOCK_HOSTLIST(i->hl);
+    return (buf);
+}
+
+char *hostlist_next_range(hostlist_iterator_t i)
+{
+    char buf[MAXHOSTRANGELEN + 1];
+    int j;
+
+    assert(i != NULL);
+    assert(i->magic == HOSTLIST_MAGIC);
+    LOCK_HOSTLIST(i->hl);
+
+    _iterator_advance_range(i);
+
+    if (i->idx > i->hl->nranges - 1) {
+        UNLOCK_HOSTLIST(i->hl);
+        return NULL;
+    }
+
+    j = i->idx;
+    _get_bracketed_list(i->hl, &j, MAXHOSTRANGELEN, buf);
+
+    UNLOCK_HOSTLIST(i->hl);
+
+    return strdup(buf);
+}
+
+int hostlist_remove(hostlist_iterator_t i)
+{
+    hostrange_t new;
+    assert(i != NULL);
+    assert(i->magic == HOSTLIST_MAGIC);
+    LOCK_HOSTLIST(i->hl);
+    new = hostrange_delete_host(i->hr, i->hr->lo + i->depth);
+    if (new) {
+        hostlist_insert_range(i->hl, new, i->idx + 1);
+        hostrange_destroy(new);
+        i->hr = i->hl->hr[++i->idx];
+        i->depth = -1;
+    } else if (hostrange_empty(i->hr)) {
+        hostlist_delete_range(i->hl, i->idx);
+    } else
+        i->depth--;
+
+    i->hl->nhosts--;
+    UNLOCK_HOSTLIST(i->hl);
+
+    return 1;
+}
+
+/* ----[ hostset functions ]---- */
+
+hostset_t hostset_create(const char *hostlist)
+{
+    hostset_t new;
+
+    if (!(new = (hostset_t) malloc(sizeof(*new))))
+        goto error1;
+
+    if (!(new->hl = hostlist_create(hostlist)))
+        goto error2;
+
+    hostlist_uniq(new->hl);
+    return new;
+
+  error2:
+    free(new);
+  error1:
+    return NULL;
+}
+
+hostset_t hostset_copy(const hostset_t set)
+{
+    hostset_t new;
+    if (!(new = (hostset_t) malloc(sizeof(*new))))
+        goto error1;
+
+    if (!(new->hl = hostlist_copy(set->hl)))
+        goto error2;
+
+    return new;
+  error2:
+    free(new);
+  error1:
+    return NULL;
+}
+
+void hostset_destroy(hostset_t set)
+{
+    if (set == NULL)
+        return;
+    hostlist_destroy(set->hl);
+    free(set);
+}
+
+/* inserts a single range object into a hostset 
+ * Assumes that the set->hl lock is already held
+ */
+static int hostset_insert_range(hostset_t set, hostrange_t hr)
+{
+    int i, n = 0;
+    int inserted = 0;
+    int retval = 0;
+    hostlist_t hl;
+
+    hl = set->hl;
+
+    if (hl->size == hl->nranges && !hostlist_expand(hl))
+        return 0;
+
+    retval = hostrange_count(hr);
+
+    for (i = 0; i < hl->nranges; i++) {
+        if (hostrange_cmp(hr, hl->hr[i]) <= 0) {
+            n = hostrange_join(hr, hl->hr[i]);
+
+            if (n >= 0) {
+                hostlist_delete_range(hl, i);
+                hl->nhosts -= n;
+            }
+
+            hostlist_insert_range(hl, hr, i);
+
+            /* now attempt to join hr[i] and hr[i-1] */
+            if (i > 0) {
+                int m = _attempt_range_join(hl, i);
+                n += m;
+            }
+            inserted = 1;
+            break;
+        }
+    }
+
+    if (inserted == 0) {
+        hl->hr[hl->nranges++] = hostrange_copy(hr);
+        n = _attempt_range_join(hl, hl->nranges - 1);
+    }
+
+    return retval - n;
+}
+
+int hostset_insert(hostset_t set, const char *hosts)
+{
+    int i, n = 0;
+    hostlist_t hl = hostlist_create(hosts);
+    if (!hl)
+        return 0;
+
+    hostlist_uniq(hl);
+    LOCK_HOSTLIST(set->hl);
+    for (i = 0; i < hl->nranges; i++) 
+        n += hostset_insert_range(set, hl->hr[i]);
+    UNLOCK_HOSTLIST(set->hl);
+    hostlist_destroy(hl);
+    return n;
+}
+
+
+/* linear search through N ranges for hostname "host"
+ * */
+static int hostset_find_host(hostset_t set, const char *host)
+{
+    int i;
+    int retval = 0;
+    hostname_t hn;
+    LOCK_HOSTLIST(set->hl);
+    hn = hostname_create(host);
+    for (i = 0; i < set->hl->nranges; i++) {
+        if (hostrange_hn_within(set->hl->hr[i], hn)) {
+            retval = 1;
+            goto done;
+        }
+    }
+  done:
+    UNLOCK_HOSTLIST(set->hl);
+    hostname_destroy(hn);
+    return retval;
+}
+
+int hostset_within(hostset_t set, const char *hosts)
+{
+    int nhosts, nfound;
+    hostlist_t hl;
+    char *hostname;
+
+    assert(set->hl->magic == HOSTLIST_MAGIC);
+
+    hl = hostlist_create(hosts);
+    nhosts = hostlist_count(hl);
+    nfound = 0;
+
+    while ((hostname = hostlist_pop(hl)) != NULL) {
+        nfound += hostset_find_host(set, hostname);
+        free(hostname);
+    }
+
+    hostlist_destroy(hl);
+
+    return (nhosts == nfound);
+}
+
+int hostset_delete(hostset_t set, const char *hosts)
+{
+    return hostlist_delete(set->hl, hosts);
+}
+
+int hostset_delete_host(hostset_t set, const char *hostname)
+{
+    return hostlist_delete_host(set->hl, hostname);
+}
+
+char *hostset_shift(hostset_t set)
+{
+    return hostlist_shift(set->hl);
+}
+
+char *hostset_pop(hostset_t set)
+{
+    return hostlist_pop(set->hl);
+}
+
+char *hostset_shift_range(hostset_t set)
+{
+    return hostlist_shift_range(set->hl);
+}
+
+char *hostset_pop_range(hostset_t set)
+{
+    return hostlist_pop_range(set->hl);
+}
+
+int hostset_count(hostset_t set)
+{
+    return hostlist_count(set->hl);
+}
+
+size_t hostset_ranged_string(hostset_t set, size_t n, char *buf)
+{
+    return hostlist_ranged_string(set->hl, n, buf);
+}
+
+size_t hostset_deranged_string(hostset_t set, size_t n, char *buf)
+{
+    return hostlist_deranged_string(set->hl, n, buf);
+}
+
+#if TEST_MAIN 
+
+int hostlist_nranges(hostlist_t hl)
+{
+    return hl->nranges;
+}
+
+int hostset_nranges(hostset_t set)
+{
+    return set->hl->nranges;
+}
+
+/* test iterator functionality on the list of hosts represented
+ * by list
+ */
+int iterator_test(char *list)
+{
+    int j;
+    char buf[1024];
+    hostlist_t hl = hostlist_create(list);
+    hostset_t set = hostset_create(list);
+
+    hostlist_iterator_t i = hostlist_iterator_create(hl);
+    hostlist_iterator_t seti = hostset_iterator_create(set);
+    hostlist_iterator_t i2 = hostlist_iterator_create(hl);
+    char *host;
+
+
+    hostlist_ranged_string(hl, 1024, buf);
+    printf("iterator_test: hl = `%s' passed in `%s'\n", buf, list);
+    host = hostlist_next(i);
+    printf("first host in list hl = `%s'\n", host);
+    free(host);
+
+    /* forge ahead three hosts with i2 */
+    for (j = 0; j < 4; j++) {
+        host = hostlist_next(i2);
+        free(host);
+    }
+
+    host = hostlist_shift(hl);
+    printf("result of shift(hl)   = `%s'\n", host);
+    free(host);
+    host = hostlist_next(i);
+    printf("next host in list hl  = `%s'\n", host);
+    free(host);
+    host = hostlist_next(i2);
+    printf("next host for i2      = `%s'\n", host);
+    free(host);
+
+    hostlist_iterator_destroy(i);
+
+    hostlist_destroy(hl);
+    hostset_destroy(set);
+    return 1;
+}
+
+int main(int ac, char **av)
+{
+    char buf[1024000];
+    int i;
+    char *str;
+
+    hostlist_t hl1, hl2, hl3;
+    hostset_t set, set1;
+    hostlist_iterator_t iter, iter2;
+
+    if (!(hl1 = hostlist_create(ac > 1 ? av[1] : NULL)))
+        perror("hostlist_create");
+    if (!(set = hostset_create(ac > 1 ? av[1] : NULL)))
+        perror("hostlist_create");
+
+    hl3 = hostlist_create("f[0-5]");
+    hostlist_delete(hl3, "f[1-3]");
+    hostlist_ranged_string(hl3, 102400, buf);
+    printf("after delete = `%s'\n", buf);
+    hostlist_destroy(hl3);
+
+    for (i = 2; i < ac; i++) {
+        hostlist_push(hl1, av[i]);
+        hostset_insert(set, av[i]);
+    }
+
+    hostlist_ranged_string(hl1, 102400, buf);
+    printf("ranged   = `%s'\n", buf);
+
+    iterator_test(buf);
+
+    hostlist_deranged_string(hl1, 10240, buf);
+    printf("deranged = `%s'\n", buf);
+
+    hostset_ranged_string(set, 1024, buf);
+    printf("hostset  = `%s'\n", buf);
+
+    hostlist_sort(hl1);
+    hostlist_ranged_string(hl1, 1024, buf);
+    printf("sorted   = `%s'\n", buf);
+
+    hostlist_uniq(hl1);
+    hostlist_ranged_string(hl1, 1024, buf);
+    printf("uniqed   = `%s'\n", buf);
+
+    hl2 = hostlist_copy(hl1);
+    printf("pop_range: ");
+    while ((str = hostlist_pop_range(hl2))) {
+        printf("`%s' ", str);
+        free(str);
+    }
+    hostlist_destroy(hl2);
+    printf("\n");
+
+    hl2 = hostlist_copy(hl1);
+    printf("shift_range: ");
+    while ((str = hostlist_shift_range(hl2))) {
+        printf("`%s' ", str);
+        free(str);
+    }
+    hostlist_destroy(hl2);
+    printf("\n");
+
+    iter = hostset_iterator_create(set);
+    iter2 = hostset_iterator_create(set);
+    hostlist_iterator_destroy(iter2);
+
+    printf("next: ");
+    while ((str = hostlist_next(iter))) {
+        printf("`%s' ", str);
+        free(str);
+    }
+    printf("\n");
+
+    hostlist_iterator_reset(iter);
+    printf("next_range: ");
+    while ((str = hostlist_next_range(iter))) {
+        printf("`%s' ", str);
+        free(str);
+    }
+    printf("\n");
+
+    printf("nranges = %d\n", hostset_nranges(set));
+
+    hostset_ranged_string(set, 1024, buf);
+    printf("set = %s\n", buf);
+
+    hostset_destroy(set);
+    hostlist_destroy(hl1);
+    return 0;
+}
+
+#endif                /* TEST_MAIN */
+
+/* 
+ * vi: tabstop=4 shiftwidth=4 expandtab 
+ */
diff --git a/contribs/pam/hostlist.h b/contribs/pam/hostlist.h
new file mode 100644
index 0000000000000000000000000000000000000000..1edbd29bf2bdec5649727b2587e7d373acecacf0
--- /dev/null
+++ b/contribs/pam/hostlist.h
@@ -0,0 +1,420 @@
+/*****************************************************************************\
+ *  $Id$
+ *****************************************************************************
+ *  $LSDId: hostlist.h 2925 2003-09-19 21:37:34Z grondo $
+ *****************************************************************************
+ *  Copyright (C) 2002 The Regents of the University of California.
+ *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
+ *  Written by Mark Grondona <mgrondona@llnl.gov>
+ *  UCRL-CODE-2002-040.
+ *  
+ *  This file is part of SLURM, a resource management program.
+ *  For details, see <http://www.llnl.gov/linux/slurm/>.
+ *  
+ *  SLURM 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 of the License, or (at your option)
+ *  any later version.
+ *  
+ *  SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
+ *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ *  details.
+ *  
+ *  You should have received a copy of the GNU General Public License along
+ *  with SLURM; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+\*****************************************************************************/
+
+#ifndef _HOSTLIST_H
+#define _HOSTLIST_H
+
+/* Notes:
+ *
+ * If WITH_LSD_FATAL_ERROR_FUNC is defined, the linker will expect to
+ * find and external lsd_fatal_error(file,line,mesg) function. By default,
+ * lsd_fatal_error(file,line,mesg) is a macro definition that outputs an
+ * error message to stderr. This macro may be redefined to invoke another
+ * routine instead. e.g.:
+ *
+ *    #define lsd_fatal_error(file,line,mesg)  \
+ *              error("%s:%s %s\n",file,line,mesg);
+ *
+ * If WITH_LSD_NOMEM_ERROR_FUNC is defined, the linker will expect to 
+ * find an external lsd_nomem_error(file,line,mesg) function. By default,
+ * lsd_nomem_error(file,line,mesg) is a macro definition that returns NULL.
+ * This macro may be redefined to invoke another routine instead.
+ *
+ * If WITH_PTHREADS is defined, these routines will be thread-safe.
+ *
+ */
+
+/* The hostlist opaque data type 
+ *
+ * A hostlist is a list of hostnames optimized for a prefixXXXX style 
+ * naming convention, where XXXX  is a decimal, numeric suffix.
+ */
+#ifndef   __hostlist_t_defined
+#  define __hostlist_t_defined
+   typedef struct hostlist * hostlist_t;
+#endif
+
+/* A hostset is a special case of a hostlist. It:
+ *
+ * 1. never contains duplicates
+ * 2. is always sorted 
+ *    (Note: sort occurs first on alphanumeric prefix -- where prefix
+ *     matches, numeric suffixes will be sorted *by value*)
+ */
+typedef struct hostset * hostset_t;
+
+/* The hostlist iterator type (may be used with a hostset as well)
+ * used for non-destructive access to hostlist members.
+ * 
+ */
+typedef struct hostlist_iterator * hostlist_iterator_t;
+
+/* ----[ hostlist_t functions: ]---- */
+
+/* ----[ hostlist creation and destruction ]---- */
+
+/*
+ * hostlist_create(): 
+ *
+ * Create a new hostlist from a string representation. 
+ *
+ * The string representation (str) may contain one or more hostnames or
+ * bracketed hostlists separated by either `,' or whitespace. A bracketed 
+ * hostlist is denoted by a common prefix followed by a list of numeric 
+ * ranges contained within brackets: e.g. "tux[0-5,12,20-25]" 
+ *
+ * Note: if this module is compiled with WANT_RECKLESS_HOSTRANGE_EXPANSION
+ * defined, a much more loose interpretation of host ranges is used. 
+ * Reckless hostrange expansion allows all of the following (in addition to 
+ * bracketed hostlists):
+ *
+ *  o tux0-5,tux12,tux20-25
+ *  o tux0-tux5,tux12,tux20-tux25
+ *  o tux0-5,12,20-25
+ *
+ * If str is NULL, and empty hostlist is created and returned. 
+ *
+ * If the create fails, hostlist_create() returns NULL.
+ *
+ * The returned hostlist must be freed with hostlist_destroy()
+ *
+ */
+hostlist_t hostlist_create(const char *hostlist);
+
+/* hostlist_copy(): 
+ *
+ * Allocate a copy of a hostlist object. Returned hostlist must be freed
+ * with hostlist_destroy.
+ */
+hostlist_t hostlist_copy(const hostlist_t hl);
+
+/* hostlist_destroy():
+ *
+ * Destroy a hostlist object. Frees all memory allocated to the hostlist.
+ */
+void hostlist_destroy(hostlist_t hl);
+
+
+/* ----[ hostlist list operations ]---- */
+
+/* hostlist_push():
+ *
+ * push a string representation of hostnames onto a hostlist.
+ *
+ * The hosts argument may take the same form as in hostlist_create()
+ *
+ * Returns the number of hostnames inserted into the list, 
+ * or 0 on failure.
+ */
+int hostlist_push(hostlist_t hl, const char *hosts);
+
+
+/* hostlist_push_host():
+ *
+ * Push a single host onto the hostlist hl. 
+ * This function is more efficient than hostlist_push() for a single
+ * hostname, since the argument does not need to be checked for ranges.
+ *
+ * return value is 1 for success, 0 for failure.
+ */
+int hostlist_push_host(hostlist_t hl, const char *host);
+
+
+/* hostlist_push_list():
+ *
+ * Push a hostlist (hl2) onto another list (hl1)
+ *
+ * Returns 1 for success, 0 for failure.
+ *
+ */
+int hostlist_push_list(hostlist_t hl1, hostlist_t hl2);
+
+
+/* hostlist_pop():
+ *
+ * Returns the string representation of the last host pushed onto the list
+ * or NULL if hostlist is empty or there was an error allocating memory.
+ * The host is removed from the hostlist.
+ *
+ * Note: Caller is responsible for freeing the returned memory.
+ */
+char * hostlist_pop(hostlist_t hl);
+
+
+char * hostlist_nth(hostlist_t hl, int n);
+
+/* hostlist_shift():
+ *
+ * Returns the string representation of the first host in the hostlist
+ * or NULL if the hostlist is empty or there was an error allocating memory.
+ * The host is removed from the hostlist.
+ *
+ * Note: Caller is responsible for freeing the returned memory.
+ */
+char * hostlist_shift(hostlist_t hl);
+
+
+/* hostlist_pop_range():
+ *
+ * Pop the last bracketed list of hosts of the hostlist hl.
+ * Returns the string representation in bracketed list form.
+ * All hosts associated with the returned list are removed
+ * from hl.
+ *
+ * Caller is responsible for freeing returned memory
+ */
+char * hostlist_pop_range(hostlist_t hl);
+
+/* hostlist_shift_range():
+ *
+ * Shift the first bracketed hostlist (improperly: range) off the
+ * hostlist hl. Returns the string representation in bracketed list
+ * form. All hosts associated with the list are removed from the
+ * hostlist.
+ *
+ * Caller is responsible for freeing returned memory.
+ */
+char * hostlist_shift_range(hostlist_t hl);
+
+
+/* hostlist_find():
+ *
+ * Searches hostlist hl for the first host matching hostname 
+ * and returns position in list if found. 
+ *
+ * Returns -1 if host is not found.
+ *
+ */
+int hostlist_find(hostlist_t hl, const char *hostname);
+
+/* hostlist_delete():
+ *
+ * Deletes all hosts in the list represented by `hosts'
+ *
+ * Returns the number of hosts successfully deleted
+ */
+int hostlist_delete(hostlist_t hl, const char *hosts);
+
+
+/* hostlist_delete_host():
+ *
+ * Deletes the first host that matches `hostname' from the hostlist hl.
+ * Note: "hostname" argument cannot contain a range of hosts 
+ *       (see hostlist_delete() for this functionality.)
+ *
+ * Returns 1 if successful, 0 if hostname is not found in list.
+ */
+int hostlist_delete_host(hostlist_t hl, const char *hostname);
+
+
+/* hostlist_delete_nth():
+ *
+ * Deletes the host from position n in the hostlist.
+ *
+ * Returns 1 if successful 0 on error.
+ *
+ */
+int hostlist_delete_nth(hostlist_t hl, int n);
+
+
+/* hostlist_count():
+ *
+ * Return the number of hosts in hostlist hl.
+ */ 
+int hostlist_count(hostlist_t hl);
+
+/* hostlist_is_empty(): return true if hostlist is empty. */
+#define hostlist_is_empty(__hl) ( hostlist_count(__hl) == 0 )
+
+/* ----[ Other hostlist operations ]---- */
+
+/* hostlist_sort():
+ * 
+ * Sort the hostlist hl.
+ *
+ */
+void hostlist_sort(hostlist_t hl);
+
+/* hostlist_uniq():
+ *
+ * Sort the hostlist hl and remove duplicate entries.
+ * 
+ */
+void hostlist_uniq(hostlist_t hl);
+
+
+/* ----[ hostlist print functions ]---- */
+
+/* hostlist_ranged_string():
+ *
+ * Write the string representation of the hostlist hl into buf,
+ * writing at most n chars. Returns the number of bytes written,
+ * or -1 if truncation occurred.
+ *
+ * The result will be NULL terminated.
+ * 
+ * hostlist_ranged_string() will write a bracketed hostlist representation
+ * where possible.
+ */
+size_t hostlist_ranged_string(hostlist_t hl, size_t n, char *buf);
+
+/* hostlist_deranged_string():
+ *
+ * Writes the string representation of the hostlist hl into buf,
+ * writing at most n chars. Returns the number of bytes written,
+ * or -1 if truncation occurred.
+ *
+ * hostlist_deranged_string() will not attempt to write a bracketed
+ * hostlist representation. Every hostname will be explicitly written.
+ */
+size_t hostlist_deranged_string(hostlist_t hl, size_t n, char *buf);
+
+
+/* ----[ hostlist utility functions ]---- */
+
+
+/* hostlist_nranges():
+ *
+ * Return the number of ranges currently held in hostlist hl.
+ */
+int hostlist_nranges(hostlist_t hl);
+
+
+/* ----[ hostlist iterator functions ]---- */
+
+/* hostlist_iterator_create():
+ *
+ * Creates and returns a hostlist iterator used for non destructive
+ * access to a hostlist or hostset. Returns NULL on failure.
+ */
+hostlist_iterator_t hostlist_iterator_create(hostlist_t hl);
+
+/* hostset_iterator_create():
+ *
+ * Same as hostlist_iterator_create(), but creates a hostlist_iterator
+ * from a hostset.
+ */
+hostlist_iterator_t hostset_iterator_create(hostset_t set);
+
+/* hostlist_iterator_destroy():
+ *
+ * Destroys a hostlist iterator.
+ */
+void hostlist_iterator_destroy(hostlist_iterator_t i);
+
+/* hostlist_iterator_reset():
+ *
+ * Reset an iterator to the beginning of the list.
+ */
+void hostlist_iterator_reset(hostlist_iterator_t i);
+
+/* hostlist_next():
+ *
+ * Returns a pointer to the  next hostname on the hostlist 
+ * or NULL at the end of the list
+ *
+ * The caller is responsible for freeing the returned memory.
+ */ 
+char * hostlist_next(hostlist_iterator_t i);
+
+
+/* hostlist_next_range():
+ *
+ * Returns the next bracketed hostlist or NULL if the iterator i is
+ * at the end of the list.
+ *
+ * The caller is responsible for freeing the returned memory.
+ *
+ */
+char * hostlist_next_range(hostlist_iterator_t i);
+
+
+/* hostlist_remove():
+ * Removes the last host returned by hostlist iterator i
+ *
+ * Returns 1 for success, 0 for failure.
+ */
+int hostlist_remove(hostlist_iterator_t i);
+
+
+/* ----[ hostset operations ]---- */
+
+/* hostset_create():
+ *
+ * Create a new hostset object from a string representation of a list of
+ * hosts. See hostlist_create() for valid hostlist forms.
+ */
+hostset_t hostset_create(const char *hostlist);
+
+/* hostset_copy():
+ *
+ * Copy a hostset object. Returned set must be freed with hostset_destroy().
+ */
+hostset_t hostset_copy(hostset_t set);
+
+/* hostset_destroy():
+ */
+void hostset_destroy(hostset_t set);
+
+/* hostset_insert():
+ * Add a host or list of hosts into hostset "set."
+ *
+ * Returns number of hosts successfully added to "set"
+ * (insertion of a duplicate is not considered successful)
+ */
+int hostset_insert(hostset_t set, const char *hosts);
+
+/* hostset_delete():
+ * Delete a host or list of hosts from hostset "set."
+ * Returns number of hosts deleted from set.
+ */
+int hostset_delete(hostset_t set, const char *hosts);
+
+/* hostset_within():
+ * Return 1 if all hosts specified by "hosts" are within the hostset "set"
+ * Retrun 0 if every host in "hosts" is not in the hostset "set"
+ */
+int hostset_within(hostset_t set, const char *hosts);
+
+/* hostset_shift():
+ * hostset equivalent to hostlist_shift()
+ */
+char * hostset_shift(hostset_t set);
+
+/* hostset_shift_range():
+ * hostset eqivalent to hostlist_shift_range()
+ */
+char * hostset_shift_range(hostset_t set);
+
+/* hostset_count():
+ * Count the number of hosts currently in hostset
+ */
+int hostset_count(hostset_t set);
+
+
+#endif /* !_HOSTLIST_H */
diff --git a/contribs/pam/pam_slurm.c b/contribs/pam/pam_slurm.c
new file mode 100644
index 0000000000000000000000000000000000000000..a5a0b2d82d25e52d728402660323042287b83ba1
--- /dev/null
+++ b/contribs/pam/pam_slurm.c
@@ -0,0 +1,419 @@
+/*****************************************************************************\
+ *  $Id$
+ *****************************************************************************
+ *  Copyright (C) 2002-2007 The Regents of the University of California.
+ *  Copyright (C) 2008-2009 Lawrence Livermore National Security.
+ *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
+ *  UCRL-CODE-2002-040.
+ *  
+ *  Written by Chris Dunlap <cdunlap@llnl.gov>
+ *         and Jim Garlick  <garlick@llnl.gov>
+ *         modified for SLURM by Moe Jette <jette@llnl.gov>.
+ *  
+ *  This file is part of pam_slurm, a PAM module for restricting access to
+ *  the compute nodes within a cluster based on information obtained from
+ *  Simple Linux Utility for Resource Managment (SLURM).  For details, see
+ *  <http://www.llnl.gov/linux/slurm/>.
+ *  
+ *  pam_slurm 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 of the License, or (at your
+ *  option) any later version.
+ *  
+ *  pam_slurm is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License along
+ *  with pam_slurm; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+\*****************************************************************************/
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include "slurm/slurm.h"
+#include "common/hostlist.h"
+
+/*  Define the externally visible functions in this file.
+ */
+#define PAM_SM_ACCOUNT
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+
+struct _options {
+    int enable_debug;
+    int enable_silence;
+    const char *msg_prefix;
+    const char *msg_suffix;
+};
+
+/*
+ *  Handle for libslurm.so
+ *
+ *  We open libslurm.so via dlopen () in order to pass the
+ *   flag RTDL_GLOBAL so that subsequently loaded modules have
+ *   access to libslurm symbols. This is pretty much only needed
+ *   for dynamically loaded modules that would otherwise be
+ *   linked against libslurm.
+ *
+ */
+static void * slurm_h = NULL;
+static int    debug   = 0;
+
+static void _log_msg(int level, const char *format, ...);
+static void _parse_args(struct _options *opts, int argc, const char **argv);
+static int  _hostrange_member(char *hostname, char *str);
+static int  _slurm_match_allocation(uid_t uid);
+static void _send_denial_msg(pam_handle_t *pamh, struct _options *opts,
+            const char *user, uid_t uid);
+
+#define DBG(msg,args...) \
+    do { \
+        if (debug) \
+           _log_msg(LOG_INFO, msg, ##args); \
+    } while (0);
+
+/**********************************\
+ *  Account Management Functions  *
+\**********************************/
+
+PAM_EXTERN int
+pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+    struct _options opts;
+    int retval;
+    char *user;
+    void *dummy;  /* needed to eliminate warning:
+                   * dereferencing type-punned pointer will break strict-aliasing rules */
+    struct passwd *pw;
+    uid_t uid;
+    int auth = PAM_PERM_DENIED;
+
+    _parse_args(&opts, argc, argv);
+    if (flags & PAM_SILENT)
+        opts.enable_silence = 1;
+
+    retval = pam_get_item(pamh, PAM_USER, (const void **) &dummy);
+    user = (char *) dummy;
+    if ((retval != PAM_SUCCESS) || (user == NULL) || (*user == '\0')) {
+        _log_msg(LOG_ERR, "unable to identify user: %s",
+            pam_strerror(pamh, retval));
+        return(PAM_USER_UNKNOWN);
+    }
+    if (!(pw = getpwnam(user))) {
+        _log_msg(LOG_ERR, "user %s does not exist", user);
+        return(PAM_USER_UNKNOWN);
+    }
+    uid = pw->pw_uid;
+
+    if (uid == 0)
+        auth = PAM_SUCCESS;
+    else if (_slurm_match_allocation(uid))
+        auth = PAM_SUCCESS;
+
+    if ((auth != PAM_SUCCESS) && (!opts.enable_silence))
+        _send_denial_msg(pamh, &opts, user, uid);
+    _log_msg(LOG_INFO, "access %s for user %s (uid=%d)",
+        (auth == PAM_SUCCESS) ? "granted" : "denied", user, uid);
+
+    return(auth);
+}
+
+
+/************************\
+ *  Internal Functions  *
+\************************/
+
+/*
+ *  Writes message described by the 'format' string to syslog.
+ */
+static void
+_log_msg(int level, const char *format, ...)
+{
+    va_list args;
+
+    openlog("pam_slurm", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
+    va_start(args, format);
+    vsyslog(level, format, args);
+    va_end(args);
+    closelog();
+    return;
+}
+
+/*
+ *  Parses module args passed via PAM's config.
+ */
+static void
+_parse_args(struct _options *opts, int argc, const char **argv)
+{
+    int i;
+
+    opts->enable_debug = 0;
+    opts->enable_silence = 0;
+    opts->msg_prefix = "";
+    opts->msg_suffix = "";
+
+    /*  rsh_kludge:
+     *  The rsh service under RH71 (rsh-0.17-2.5) truncates the first char
+     *  of this msg.  The rsh client sends 3 NUL-terminated ASCII strings:
+     *  client-user-name, server-user-name, and command string.  The server
+     *  then validates the user.  If the user is valid, it responds with a
+     *  1-byte zero; o/w, it responds with a 1-byte one followed by an ASCII
+     *  error message and a newline.  RH's server is using the default PAM
+     *  conversation function which doesn't prepend the message with a
+     *  single-byte error code.  As a result, the client receives a string,
+     *  interprets the first byte as a non-zero status, and treats the
+     *  remaining string as an error message.  The rsh_kludge prepends a
+     *  newline which will be interpreted by the rsh client as an error status.
+     *
+     *  rlogin_kludge:
+     *  The rlogin service under RH71 (rsh-0.17-2.5) does not perform a
+     *  carriage-return after the PAM error message is displayed which results
+     *  in the "staircase-effect" of the next message. The rlogin_kludge
+     *  appends a carriage-return to prevent this.
+     */
+    for (i=0; i<argc; i++) {
+        if (!strcmp(argv[i], "debug"))
+            opts->enable_debug = debug = 1;
+        else if (!strcmp(argv[i], "no_warn"))
+            opts->enable_silence = 1;
+        else if (!strcmp(argv[i], "rsh_kludge"))
+            opts->msg_prefix = "\n";
+        else if (!strcmp(argv[i], "rlogin_kludge"))
+            opts->msg_suffix = "\r";
+        else
+            _log_msg(LOG_ERR, "unknown option [%s]", argv[i]);
+    }
+    return;
+}
+
+/* 
+ *  Return 1 if 'hostname' is a member of 'str', a SLURM-style host list as 
+ *  returned by SLURM datatbase queries, else 0.  The 'str' argument is
+ *  truncated to the base prefix as a side-effect.
+ */
+static int
+_hostrange_member(char *hostname, char *str)
+{
+    hostlist_t hl;
+    int found_host;
+
+    if (!*hostname || !*str)
+        return 0;
+
+    if ((hl = hostlist_create(str)) == NULL)
+        return 0;
+    found_host = hostlist_find(hl, hostname);
+    hostlist_destroy(hl);
+
+    if (found_host == -1)
+        return 0;
+    else
+        return 1;
+}
+
+/*
+ *  Wrapper for SLURM API function slurm_load_jobs ()
+ */
+int _slurm_load_jobs (job_info_msg_t **msgp)
+{
+    static int (*load_jobs) (time_t, job_info_msg_t **, uint16_t);
+
+    if (!(load_jobs = dlsym (slurm_h, "slurm_load_jobs"))) {
+        _log_msg (LOG_ERR, "Unable to resolve slurm_load_jobs\n");
+        return -1;
+    }
+    
+    return load_jobs ((time_t) NULL, msgp, (uint16_t) SHOW_ALL);
+}
+
+/*
+ *  Wrapper for SLURM API function slurm_strerror ()
+ */
+char * _slurm_strerror (int errnum)
+{
+    static char * (*f) (int);
+
+    if (!(f = dlsym (slurm_h, "slurm_strerror"))) {
+        _log_msg (LOG_ERR, "Unable to resolve slurm_strerror\n");
+        return "unknown error";
+    }
+
+    return f (errnum);
+}
+
+/*
+ *  Wrapper for slurm_free_job_info_msg ()
+ */
+void _free_msg (job_info_msg_t *msg)
+{
+    static void (*free_msg) (job_info_msg_t *);
+
+    if (!(free_msg = dlsym (slurm_h, "slurm_free_job_info_msg"))) {
+        _log_msg (LOG_ERR, "Unable to resolve slurm_free_job...\n");
+        return;
+    }
+
+    free_msg (msg);
+
+    return;
+}
+
+/*
+ *  Query the SLURM database to find out if 'uid' has been allocated 
+ *  this node. If so, return 1 indicating that 'uid' is authorized to 
+ *  this node else return 0.  
+ */
+static int 
+_slurm_match_allocation(uid_t uid)
+{
+    int authorized = 0, i;
+    char hostname[MAXHOSTNAMELEN], *p;
+    job_info_msg_t * msg;
+
+    if (gethostname(hostname, sizeof(hostname)) < 0) {
+        _log_msg(LOG_ERR, "gethostname: %m");
+        return 0;
+    }
+    if ((p = strchr(hostname, '.')))
+        *p = '\0';
+
+    DBG ("does uid %ld have \"%s\" allocated", uid, hostname);
+
+    if (_slurm_load_jobs(&msg) < 0) {
+        _log_msg(LOG_ERR, "slurm_load_jobs: %s", _slurm_strerror(errno));
+        return 0;
+    }
+
+    DBG ("slurm_load_jobs returned %d records", msg->record_count);
+
+    for (i = 0; i < msg->record_count; i++) {
+        job_info_t *j = &msg->job_array[i];
+
+        if (  (j->user_id == uid) && (j->job_state == JOB_RUNNING)) {
+
+           DBG ("jobid %ld: nodes=\"%s\"", j->job_id, j->nodes);
+
+           if (_hostrange_member(hostname, j->nodes) ) {
+                 DBG ("user %ld allocated node %s in job %ld", 
+                      uid, hostname, j->job_id);
+                authorized = 1;
+                break;
+            }
+        }
+    }
+
+    _free_msg (msg);
+
+    return authorized;
+}
+
+/*
+ *  Sends a message to the application informing the user
+ *  that access was denied due to SLURM.
+ */
+static void
+_send_denial_msg(pam_handle_t *pamh, struct _options *opts,
+                 const char *user, uid_t uid)
+{
+    int retval;
+    struct pam_conv *conv;
+    void *dummy;    /* needed to eliminate warning:
+                     * dereferencing type-punned pointer will break strict-aliasing rules */
+    int n;
+    char str[PAM_MAX_MSG_SIZE];
+    struct pam_message msg[1];
+    const struct pam_message *pmsg[1];
+    struct pam_response *prsp;
+
+    /*  Get conversation function to talk with app.
+     */
+    retval = pam_get_item(pamh, PAM_CONV, (const void **) &dummy);
+    conv = (struct pam_conv *) dummy;
+    if (retval != PAM_SUCCESS) {
+        _log_msg(LOG_ERR, "unable to get pam_conv: %s",
+            pam_strerror(pamh, retval));
+        return;
+    }
+
+    /*  Construct msg to send to app.
+     */
+    n = snprintf(str, sizeof(str),
+        "%sAccess denied: user %s (uid=%d) has no active jobs.%s",
+        opts->msg_prefix, user, uid, opts->msg_suffix);
+    if ((n < 0) || (n >= sizeof(str)))
+        _log_msg(LOG_ERR, "exceeded buffer for pam_conv message");
+    msg[0].msg_style = PAM_ERROR_MSG;
+    msg[0].msg = str;
+    pmsg[0] = &msg[0];
+    prsp = NULL;
+
+    /*  Send msg to app and free the (meaningless) rsp.
+     */
+    retval = conv->conv(1, pmsg, &prsp, conv->appdata_ptr);
+    if (retval != PAM_SUCCESS)
+        _log_msg(LOG_ERR, "unable to converse with app: %s",
+            pam_strerror(pamh, retval));
+    if (prsp != NULL)
+        _pam_drop_reply(prsp, 1);
+
+    return;
+}
+
+/* 
+ * Dynamically open system's libslurm.so with RTLD_GLOBAL flag.
+ *  This allows subsequently loaded modules access to libslurm symbols.
+ */
+extern void _init (void)
+{
+    if (slurm_h)
+        return;
+
+	if (!(slurm_h = dlopen("libslurm.so", RTLD_NOW|RTLD_GLOBAL))) 
+		_log_msg (LOG_ERR, "Unable to dlopen libslurm: %s\n", dlerror ());
+
+    return;
+}
+
+extern void _fini (void)
+{
+    if (slurm_h)
+        dlclose (slurm_h);
+    return;
+}
+
+
+/*************************************\
+ *  Statically Loaded Module Struct  *
+\*************************************/
+
+#ifdef PAM_STATIC
+struct pam_module _pam_rms_modstruct = {
+    "pam_slurm",
+    NULL,
+    NULL,
+    pam_sm_acct_mgmt,
+    NULL,
+    NULL,
+    NULL,
+};
+#endif /* PAM_STATIC */
+
+/*
+ * vi: sw=4 ts=4 expandtab
+ */
diff --git a/contribs/pam/pam_slurm.spec b/contribs/pam/pam_slurm.spec
new file mode 100644
index 0000000000000000000000000000000000000000..8c40cc197b74a1779be17d4d5cfbcebc96dc7008
--- /dev/null
+++ b/contribs/pam/pam_slurm.spec
@@ -0,0 +1,48 @@
+##
+# $Id$
+##
+
+Name:		pam_slurm
+Version:	1.3
+Release:	1
+
+Summary:	PAM module for restricting access to compute nodes via SLURM.
+Group:		System Environment/Base
+License:	GPL
+URL:		http://www.llnl.gov/linux/slurm/
+
+BuildRoot:	%{_tmppath}/%{name}-%{version}
+BuildRequires: slurm-devel pam-devel
+Requires: slurm 
+
+Source0:	%{name}-%{version}.tgz
+
+%description
+This module restricts access to compute nodes in a cluster where the Simple 
+Linux Utility for Resource Managment (SLURM) is in use.  Access is granted
+to root, any user with an SLURM-launched job currently running on the node,
+or any user who has allocated resources on the node according to the SLURM
+database.
+
+%prep
+%setup
+
+%build
+make CFLAGS="$RPM_OPT_FLAGS"
+
+%install
+rm -rf "$RPM_BUILD_ROOT"
+mkdir -p "$RPM_BUILD_ROOT"
+# make install DESTDIR="$RPM_BUILD_ROOT"
+# for multilib support, we install into /%{_lib}/security instead
+install -D -m0755 pam_slurm.so $RPM_BUILD_ROOT/%{_lib}/security/pam_slurm.so
+
+%clean
+rm -rf "$RPM_BUILD_ROOT"
+
+%files
+%defattr(-,root,root,0755)
+%doc COPYING
+%doc DISCLAIMER
+%doc README
+/%{_lib}/security/pam_slurm.so