diff --git a/Makefile.am b/Makefile.am index fb8e51d0..dc5d1ffe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = src/client src/server src/fe doc +SUBDIRS = src/client src/server src/fe src/config_helper doc if BLD_SLURMPLUGIN SUBDIRS += src/slurm_plugin endif diff --git a/Makefile.in b/Makefile.in index 9e4b21b3..c4a8089c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -166,8 +166,8 @@ am__define_uniq_tagged_files = \ ETAGS = etags CTAGS = ctags CSCOPE = cscope -DIST_SUBDIRS = src/client src/server src/fe doc src/slurm_plugin \ - src/flux testsuite +DIST_SUBDIRS = src/client src/server src/fe src/config_helper doc \ + src/slurm_plugin src/flux testsuite am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(top_srcdir)/scripts/ar-lib $(top_srcdir)/scripts/compile \ $(top_srcdir)/scripts/config.guess \ @@ -380,8 +380,8 @@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -SUBDIRS = src/client src/server src/fe doc $(am__append_1) \ - $(am__append_2) $(am__append_3) +SUBDIRS = src/client src/server src/fe src/config_helper doc \ + $(am__append_1) $(am__append_2) $(am__append_3) all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive diff --git a/configure b/configure index 0b4da648..43bf8dd5 100755 --- a/configure +++ b/configure @@ -5775,7 +5775,7 @@ $as_echo "$ac_cv_path_SED" >&6; } ac_config_headers="$ac_config_headers config.h" -ac_config_files="$ac_config_files src/flux/Makefile src/slurm_plugin/Makefile doc/Makefile testsuite/Makefile Makefile" +ac_config_files="$ac_config_files src/flux/Makefile src/slurm_plugin/Makefile src/config_helper/Makefile doc/Makefile testsuite/Makefile Makefile" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } @@ -20131,6 +20131,7 @@ do "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "src/flux/Makefile") CONFIG_FILES="$CONFIG_FILES src/flux/Makefile" ;; "src/slurm_plugin/Makefile") CONFIG_FILES="$CONFIG_FILES src/slurm_plugin/Makefile" ;; + "src/config_helper/Makefile") CONFIG_FILES="$CONFIG_FILES src/config_helper/Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "testsuite/Makefile") CONFIG_FILES="$CONFIG_FILES testsuite/Makefile" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; diff --git a/configure.ac b/configure.ac index b2c519d4..e778fc91 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,7 @@ AC_PROG_CC AC_PROG_CXX AC_PROG_SED AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_FILES([src/flux/Makefile src/slurm_plugin/Makefile doc/Makefile testsuite/Makefile Makefile]) +AC_CONFIG_FILES([src/flux/Makefile src/slurm_plugin/Makefile src/config_helper/Makefile doc/Makefile testsuite/Makefile Makefile]) AC_PROG_LN_S LT_INIT diff --git a/src/client/crash_handler/crash_handler.c b/src/client/crash_handler/crash_handler.c index ba521d9c..76fcd924 100644 --- a/src/client/crash_handler/crash_handler.c +++ b/src/client/crash_handler/crash_handler.c @@ -434,6 +434,7 @@ int crash_handler_install(int global_rank, int ldcsid_in) sigaddset(&sa.sa_mask, SIGABRT); const int sigs[] = { SIGSEGV, SIGBUS, SIGFPE, SIGILL, SIGABRT }; + crash_sigchain_activate(); for (size_t i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) { /* If a signal handler was already installed by the time Spindle registers its signal handler, we save it to use in diff --git a/src/client/crash_handler/crash_sigchain.c b/src/client/crash_handler/crash_sigchain.c index 7e4351e5..c9de0f33 100644 --- a/src/client/crash_handler/crash_sigchain.c +++ b/src/client/crash_handler/crash_sigchain.c @@ -72,8 +72,18 @@ struct app_disposition { static const int OWNED_SIGS[] = { SIGSEGV, SIGBUS, SIGFPE, SIGILL, SIGABRT }; +/* Set once the crash handler has installed its handlers. */ +static int sigchain_active = 0; + +void crash_sigchain_activate(void) +{ + sigchain_active = 1; +} + int crash_sigchain_is_owned(int sig) { + if (!sigchain_active) + return 0; for (size_t i = 0; i < sizeof(OWNED_SIGS) / sizeof(OWNED_SIGS[0]); i++) { if (OWNED_SIGS[i] == sig) return 1; diff --git a/src/client/crash_handler/crash_sigchain.h b/src/client/crash_handler/crash_sigchain.h index d6d2fa39..273dcfaf 100644 --- a/src/client/crash_handler/crash_sigchain.h +++ b/src/client/crash_handler/crash_sigchain.h @@ -26,6 +26,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #define CRASH_CHAIN_HANDLED 1 /* application handler ran and returned */ #define CRASH_CHAIN_IGNORED 2 /* application disposition is SIG_IGN */ +void crash_sigchain_activate(void); int crash_sigchain_is_owned(int sig); void crash_sigchain_register_existing_handler(int sig, const struct sigaction *handler_old); int crash_sigchain_chain_to_app(int sig, siginfo_t *info, void *ucontext); diff --git a/src/config_helper/Makefile.am b/src/config_helper/Makefile.am new file mode 100644 index 00000000..6b5ea773 --- /dev/null +++ b/src/config_helper/Makefile.am @@ -0,0 +1,24 @@ +bin_PROGRAMS = spindle-config-helper + +spindle_config_helper_SOURCES = spindle-config-helper.cc + +spindle_config_helper_CPPFLAGS = -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/fe/startup \ + -I$(top_srcdir)/src/fe/logging \ + -I$(top_srcdir)/src/utils \ + -I$(top_srcdir)/src/server/cache \ + -I$(top_srcdir)/src/server/comlib \ + -I$(top_srcdir)/src/cobo \ + -I$(top_srcdir)/src/fe/comlib \ + -DPKGSYSCONFDIR=\"$(PKGSYSCONF_DIR)\" \ + $(MPI_CXXFLAGS) + +spindle_config_helper_CXXFLAGS = $(MPI_CXXFLAGS) + +spindle_config_helper_LDFLAGS = $(MPI_CXXLDFLAGS) + +spindle_config_helper_LDADD = $(top_builddir)/src/fe/startup/libconfigparse.a \ + $(top_builddir)/src/fe/logging/libspindleflogc.la \ + -lpthread + +man_MANS = man/spindle-config-helper.1 diff --git a/src/config_helper/Makefile.in b/src/config_helper/Makefile.in new file mode 100644 index 00000000..ce59e413 --- /dev/null +++ b/src/config_helper/Makefile.in @@ -0,0 +1,803 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 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@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@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@ +bin_PROGRAMS = spindle-config-helper$(EXEEXT) +subdir = src/config_helper +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.common.ac \ + $(top_srcdir)/m4/ax_tls.m4 $(top_srcdir)/m4/lmon.m4 \ + $(top_srcdir)/m4/lx_find_mpi.m4 $(top_srcdir)/m4/runconf.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" +PROGRAMS = $(bin_PROGRAMS) +am_spindle_config_helper_OBJECTS = \ + spindle_config_helper-spindle-config-helper.$(OBJEXT) +spindle_config_helper_OBJECTS = $(am_spindle_config_helper_OBJECTS) +spindle_config_helper_DEPENDENCIES = \ + $(top_builddir)/src/fe/startup/libconfigparse.a \ + $(top_builddir)/src/fe/logging/libspindleflogc.la +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +spindle_config_helper_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(spindle_config_helper_CXXFLAGS) $(CXXFLAGS) \ + $(spindle_config_helper_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/scripts/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = \ + ./$(DEPDIR)/spindle_config_helper-spindle-config-helper.Po +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(spindle_config_helper_SOURCES) +DIST_SOURCES = $(spindle_config_helper_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +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 = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +man1dir = $(mandir)/man1 +NROFF = nroff +MANS = $(man_MANS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/scripts/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AR_FLAGS = @AR_FLAGS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BE_CC = @BE_CC@ +BE_CFLAGS = @BE_CFLAGS@ +BE_CPP = @BE_CPP@ +BE_CPPFLAGS = @BE_CPPFLAGS@ +BE_CXX = @BE_CXX@ +BE_CXXCPP = @BE_CXXCPP@ +BE_CXXFLAGS = @BE_CXXFLAGS@ +BE_LDFLAGS = @BE_LDFLAGS@ +BE_LIBS = @BE_LIBS@ +BE_host = @BE_host@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FLUX_CORE_CFLAGS = @FLUX_CORE_CFLAGS@ +FLUX_CORE_LIBS = @FLUX_CORE_LIBS@ +FLUX_HOSTLIST_CFLAGS = @FLUX_HOSTLIST_CFLAGS@ +FLUX_HOSTLIST_LIBS = @FLUX_HOSTLIST_LIBS@ +GREP = @GREP@ +HAVE_LMON = @HAVE_LMON@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LAUNCHMON_INC = @LAUNCHMON_INC@ +LAUNCHMON_LIB = @LAUNCHMON_LIB@ +LAUNCHMON_RMCOMM = @LAUNCHMON_RMCOMM@ +LAUNCHMON_STATIC_LIBS = @LAUNCHMON_STATIC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MPICC = @MPICC@ +MPICXX = @MPICXX@ +MPI_CFLAGS = @MPI_CFLAGS@ +MPI_CLDFLAGS = @MPI_CLDFLAGS@ +MPI_CXXFLAGS = @MPI_CXXFLAGS@ +MPI_CXXLDFLAGS = @MPI_CXXLDFLAGS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +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_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKGSYSCONF_DIR = @PKGSYSCONF_DIR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SCONTROL_ABSPATH = @SCONTROL_ABSPATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SINFO_ABSPATH = @SINFO_ABSPATH@ +SRUN_PATH = @SRUN_PATH@ +STATICFLAG = @STATICFLAG@ +STRIP = @STRIP@ +TESTRM = @TESTRM@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +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@ +fluxplugin_ldflags = @fluxplugin_ldflags@ +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@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +shell_plugindir = @shell_plugindir@ +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@ +spindle_config_helper_SOURCES = spindle-config-helper.cc +spindle_config_helper_CPPFLAGS = -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/fe/startup \ + -I$(top_srcdir)/src/fe/logging \ + -I$(top_srcdir)/src/utils \ + -I$(top_srcdir)/src/server/cache \ + -I$(top_srcdir)/src/server/comlib \ + -I$(top_srcdir)/src/cobo \ + -I$(top_srcdir)/src/fe/comlib \ + -DPKGSYSCONFDIR=\"$(PKGSYSCONF_DIR)\" \ + $(MPI_CXXFLAGS) + +spindle_config_helper_CXXFLAGS = $(MPI_CXXFLAGS) +spindle_config_helper_LDFLAGS = $(MPI_CXXLDFLAGS) +spindle_config_helper_LDADD = $(top_builddir)/src/fe/startup/libconfigparse.a \ + $(top_builddir)/src/fe/logging/libspindleflogc.la \ + -lpthread + +man_MANS = man/spindle-config-helper.1 +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .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 src/config_helper/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/config_helper/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + 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 +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +spindle-config-helper$(EXEEXT): $(spindle_config_helper_OBJECTS) $(spindle_config_helper_DEPENDENCIES) $(EXTRA_spindle_config_helper_DEPENDENCIES) + @rm -f spindle-config-helper$(EXEEXT) + $(AM_V_CXXLD)$(spindle_config_helper_LINK) $(spindle_config_helper_OBJECTS) $(spindle_config_helper_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spindle_config_helper-spindle-config-helper.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +spindle_config_helper-spindle-config-helper.o: spindle-config-helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(spindle_config_helper_CPPFLAGS) $(CPPFLAGS) $(spindle_config_helper_CXXFLAGS) $(CXXFLAGS) -MT spindle_config_helper-spindle-config-helper.o -MD -MP -MF $(DEPDIR)/spindle_config_helper-spindle-config-helper.Tpo -c -o spindle_config_helper-spindle-config-helper.o `test -f 'spindle-config-helper.cc' || echo '$(srcdir)/'`spindle-config-helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/spindle_config_helper-spindle-config-helper.Tpo $(DEPDIR)/spindle_config_helper-spindle-config-helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='spindle-config-helper.cc' object='spindle_config_helper-spindle-config-helper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(spindle_config_helper_CPPFLAGS) $(CPPFLAGS) $(spindle_config_helper_CXXFLAGS) $(CXXFLAGS) -c -o spindle_config_helper-spindle-config-helper.o `test -f 'spindle-config-helper.cc' || echo '$(srcdir)/'`spindle-config-helper.cc + +spindle_config_helper-spindle-config-helper.obj: spindle-config-helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(spindle_config_helper_CPPFLAGS) $(CPPFLAGS) $(spindle_config_helper_CXXFLAGS) $(CXXFLAGS) -MT spindle_config_helper-spindle-config-helper.obj -MD -MP -MF $(DEPDIR)/spindle_config_helper-spindle-config-helper.Tpo -c -o spindle_config_helper-spindle-config-helper.obj `if test -f 'spindle-config-helper.cc'; then $(CYGPATH_W) 'spindle-config-helper.cc'; else $(CYGPATH_W) '$(srcdir)/spindle-config-helper.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/spindle_config_helper-spindle-config-helper.Tpo $(DEPDIR)/spindle_config_helper-spindle-config-helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='spindle-config-helper.cc' object='spindle_config_helper-spindle-config-helper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(spindle_config_helper_CPPFLAGS) $(CPPFLAGS) $(spindle_config_helper_CXXFLAGS) $(CXXFLAGS) -c -o spindle_config_helper-spindle-config-helper.obj `if test -f 'spindle-config-helper.cc'; then $(CYGPATH_W) 'spindle-config-helper.cc'; else $(CYGPATH_W) '$(srcdir)/spindle-config-helper.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man1: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man1dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.1[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ + done; } + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man1dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.1[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(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 "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$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 $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; 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: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/spindle_config_helper-spindle-config-helper.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man1 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/spindle_config_helper-spindle-config-helper.Po + -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-binPROGRAMS uninstall-man + +uninstall-man: uninstall-man1 + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + 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-man1 \ + install-pdf install-pdf-am 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 tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-man uninstall-man1 + +.PRECIOUS: Makefile + + +# 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/src/config_helper/man/spindle-config-helper.1 b/src/config_helper/man/spindle-config-helper.1 new file mode 100644 index 00000000..cb6446f4 --- /dev/null +++ b/src/config_helper/man/spindle-config-helper.1 @@ -0,0 +1,224 @@ +.TH SPINDLE-CONFIG-HELPER 1 "November 2026" "Spindle 0.13" "User Commands" +.SH NAME +spindle-config-helper \- generate and validate Spindle configuration files +.SH SYNOPSIS +.B spindle-config-helper +[\fB-h\fR|\fB--help\fR] [\fB-V\fR|\fB--version\fR] [\fB\-\-\fR \fISPINDLE_OPTIONS\fR] +.SH DESCRIPTION +.B spindle-config-helper +is a utility for generating Spindle configuration files in HPC +environments. It analyzes the local system to automatically detect optimal +configuration settings and outputs a +.I spindle.conf +file suitable for the environment. +.PP +The tool performs the following analysis: +.IP \(bu 2 +Detects local vs. remote filesystem mounts (NFS, Lustre, GPFS, tmpfs, overlay) +.IP \(bu 2 +Validates +.B $TMPDIR +availability and recommends appropriate paths +.IP \(bu 2 +Tests SSH connectivity between MPI ranks to verify compute node access +.IP \(bu 2 +Discovers Python installations via +.B $PYTHONPREFIX +and Python's +.B sys.prefix +and +.B sys.path +.IP \(bu 2 +Generates +.I localprefix +from top-level local directories +.IP \(bu 2 +Validates path writability and accessibility +.IP \(bu 2 +Tests Munge availability and functionality +.PP +The tool must be run as an MPI program on at least two compute nodes +to provide accurate environment analysis, particularly for SSH connectivity testing. +.SH OPTIONS +.TP +.BR \-h ", " \-\-help +Display usage information and exit. +.TP +.BR \-V ", " \-\-version +Display version information and exit. +.SH INSTRUCTIONS +To generate a Spindle configuration file: +.IP 1. 4 +Set +.B PYTHONPREFIX +to the Python installation your programs will use. +.IP 2. 4 +Set +.B TMPDIR +to node-local temporary storage (e.g., +.IR /tmp ). +.IP 3. 4 +Run +.B spindle-config-helper +as you would any other parallel program, e.g.: +.RS 4 +.nf +flux run -N2 -n2 spindle-config-helper +srun -N2 -n2 spindle-config-helper +.fi +.RE +.IP 4. 4 +Review the output, then redirect or copy/paste to +.IR ~/.spindle/spindle.conf . +.PP +See the tool's output for explanations of how it chooses paths and validates the environment. +.SH CONFIGURATION OUTPUT +The tool generates a minimal configuration with four essential path variables: +.TP +.B commpaths +Local directories for inter-process communication. Based on +.B $TMPDIR +if set and valid, otherwise recommends a suitable local filesystem. +.TP +.B cachepaths +Local directories for file caching. Uses similar logic to +.BR commpaths . +.TP +.B python-prefix +Python installation directories discovered via +.B $PYTHONPREFIX +and Python's +.B sys.prefix +and +.BR sys.path . +Validates that paths contain Python packages. +.TP +.B localprefix +Colon-separated list of top-level directories that should be excluded from +caching. Generated from local (non-remote) filesystem mount points. +Overlay filesystems are treated as non-local. +.PP +The output includes warnings and notes as comments: +.IP \(bu 2 +.B WARNING: +Issues that require attention (missing environment variables, failed tests, invalid paths) +.IP \(bu 2 +.B Note: +Successful validations and test results +.SH ENVIRONMENT +.TP +.B PYTHONPREFIX +Python installation prefix. Preferred source for Python path discovery. +.TP +.B TMPDIR +Preferred directory for +.B commpaths +and +.BR cachepaths . +If not set or invalid, the tool will issue a warning and attempt to find +suitable alternatives. +.SH SPINDLE OPTIONS +Any valid Spindle configuration option can be passed after +.BR \-\- . +These options are parsed using Spindle's built-in option handling. +Use +.B spindle-config-helper \-\- \-\-usage +to see all available options. +.SH EXAMPLES +.TP +Generate a new configuration to stdout: +.nf +.RS +spindle-config-helper +.RE +.fi +.TP +Generate configuration under MPI: +.nf +.RS +flux run -N2 -n2 spindle-config-helper > ~/.spindle/spindle.conf +srun -N2 -n2 spindle-config-helper | tee spindle.conf +.RE +.fi +.TP +Check warnings in output: +.nf +.RS +spindle-config-helper | grep WARNING +.RE +.fi +.TP +View all available Spindle options: +.nf +.RS +spindle-config-helper \-\- \-\-usage +.RE +.fi +.SH FILES +.TP +.I /etc/spindle.conf +System-wide Spindle configuration file. +.TP +.I ~/.spindle/spindle.conf +Per-user Spindle configuration file (recommended location). +.TP +.I /proc/mounts +Used to detect filesystem mount points and types. +.SH EXIT STATUS +.TP +.B 0 +Success. Valid configuration was generated. +.TP +.B 1 +Error. Invalid arguments or configuration error occurred. +.SH NOTES +.SS MPI Requirement +The tool must be run as an MPI program on at least two nodes for accurate results. +Running on a single node will issue a warning, as SSH ring testing requires +multiple nodes to properly validate inter-node connectivity. +.SS Path Validation +The tool validates paths by checking: +.IP \(bu 2 +Filesystem is local (not NFS, Lustre, GPFS, or overlay) +.IP \(bu 2 +Path is writable by the current user +.IP \(bu 2 +Parent directories exist +.PP +Paths that fail validation are filtered out and reported in warnings. +.SS SSH Ring Testing +When running under MPI with multiple nodes, each rank tests SSH connectivity +to the next rank in a ring pattern (highest rank connects to rank 0). +This verifies that compute nodes can communicate via SSH without password prompts. +.SS Python Discovery +Python installations are discovered in order of preference: +.IP 1. 4 +.B $PYTHONPREFIX +environment variable +.IP 2. 4 +Python's +.B sys.prefix +(queried from Python itself) +.IP 3. 4 +Python's +.B sys.path +entries +.PP +The tool validates that discovered paths contain actual Python packages, +not just the Python binary. +.SS Overlay Filesystems +Overlay filesystems (detected via +.IR /proc/mounts ) +are treated as non-local and excluded from +.BR commpaths , +.BR cachepaths , +and +.BR localprefix . +.SH SEE ALSO +.BR spindle (1) +.PP +Spindle documentation: https://github.com/hpc/Spindle +.SH AUTHOR +The Spindle development team at Lawrence Livermore National Laboratory. +.SH BUGS +Report bugs to the Spindle issue tracker at https://github.com/hpc/Spindle/issues diff --git a/src/config_helper/spindle-config-helper.cc b/src/config_helper/spindle-config-helper.cc new file mode 100644 index 00000000..4156f60b --- /dev/null +++ b/src/config_helper/spindle-config-helper.cc @@ -0,0 +1,1490 @@ +/* +This file is part of Spindle. For copyright information see the COPYRIGHT +file in the top level directory, or at +https://github.com/hpc/Spindle/blob/master/COPYRIGHT + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License (as published by the Free Software +Foundation) version 2.1 dated February 1999. This program is distributed in the +hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED +WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms +and conditions of the GNU Lesser General Public License for more details. You should +have received a copy of the GNU Lesser General Public License along with this +program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "config.h" +#include "config_mgr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "keyfile.h" +} + +struct MountPoint { + std::string path; + std::string fstype; + bool is_remote; + uint64_t avail_gb; + + MountPoint(const std::string& p, const std::string& fs) + : path(p), fstype(fs), is_remote(false), avail_gb(0) {} +}; + +static bool is_remote_filesystem(const std::string& fstype) +{ + static const char* remote_types[] = { + "nfs", "nfs4", "lustre", "gpfs", "panfs", + "ceph", "glusterfs", "afs", "smbfs", "cifs", + "overlay", // Overlay filesystems are considered non-local + nullptr + }; + + for (int i = 0; remote_types[i]; i++) { + if (fstype == remote_types[i] || + fstype.find(std::string("fuse.") + remote_types[i]) == 0) { + return true; + } + } + return false; +} + +static bool is_kernel_filesystem(const std::string& path) +{ + // Hardcode known kernel filesystem paths + // Note: /dev/shm is NOT a kernel filesystem, it's tmpfs + return path == "/proc" || path == "/sys" || + (path.find("/proc/") == 0) || + (path.find("/sys/") == 0) || + (path == "/dev" || (path.find("/dev/") == 0 && path != "/dev/shm")); +} + +static bool should_ignore_mount(const std::string& path, const std::string& fstype) +{ + // Ignore virtual/system filesystems + if (fstype == "proc" || fstype == "sysfs" || fstype == "devtmpfs" || + fstype == "devpts" || fstype == "securityfs" || fstype == "pstore" || + fstype == "autofs" || fstype == "mqueue" || fstype == "hugetlbfs" || + fstype.find("cgroup") == 0 || fstype.find("bpf") == 0) { + return true; + } + + // Ignore system tmpfs mounts (but NOT /dev/shm which is useful) + if (fstype == "tmpfs" && (path == "/run" || path == "/sys/fs/cgroup" || + path.find("/run/") == 0 || path.find("/sys/") == 0 || + (path.find("/dev/") == 0 && path != "/dev/shm"))) { + return true; + } + + return false; +} + +static uint64_t get_available_gb(const std::string& path) +{ + struct statvfs stat; + if (statvfs(path.c_str(), &stat) != 0) { + return 0; + } + + uint64_t avail_bytes = (uint64_t)stat.f_bavail * stat.f_frsize; + return avail_bytes / (1024ULL * 1024 * 1024); +} + +static std::vector parse_mounts() +{ + std::vector mounts; + std::ifstream mounts_file("/proc/mounts"); + std::string line; + std::set seen_paths; + + while (std::getline(mounts_file, line)) { + std::istringstream iss(line); + std::string device, path, fstype, options; + + if (!(iss >> device >> path >> fstype >> options)) { + continue; + } + + if (should_ignore_mount(path, fstype)) { + continue; + } + + // Skip duplicate paths + if (seen_paths.count(path)) { + continue; + } + seen_paths.insert(path); + + MountPoint mp(path, fstype); + + // Overlay filesystems are typically local (container backing storage) + if (fstype == "overlay") { + mp.is_remote = false; + } else { + mp.is_remote = is_remote_filesystem(fstype); + } + + if (!mp.is_remote) { + mp.avail_gb = get_available_gb(path); + + // Skip local mounts with < 1GB available + if (mp.avail_gb < 1) { + continue; + } + + // Check if writable + if (access(path.c_str(), W_OK) != 0) { + continue; + } + } + + mounts.push_back(mp); + } + + // Sort by path length (longest first) for prefix matching + std::sort(mounts.begin(), mounts.end(), + [](const MountPoint& a, const MountPoint& b) { + return a.path.length() > b.path.length(); + }); + + return mounts; +} + +enum ExpandResult { + EXPAND_OK, + EXPAND_UNDEFINED_VAR, + EXPAND_EMPTY, + EXPAND_ERROR +}; + +struct ExpandedPath { + std::string expanded; + ExpandResult result; +}; + +static ExpandedPath expand_path(const std::string& path) +{ + ExpandedPath ep; + + if (path.empty()) { + ep.expanded = path; + ep.result = EXPAND_EMPTY; + return ep; + } + + // Use wordexp for shell expansion (handles $VAR, ~, etc.) + wordexp_t exp_result; + int ret = wordexp(path.c_str(), &exp_result, WRDE_NOCMD | WRDE_UNDEF); + + if (ret == WRDE_BADVAL) { + // Undefined variable + ep.expanded = ""; + ep.result = EXPAND_UNDEFINED_VAR; + return ep; + } else if (ret != 0) { + // Other expansion error + ep.expanded = ""; + ep.result = EXPAND_ERROR; + return ep; + } + + if (exp_result.we_wordc > 0 && exp_result.we_wordv[0] && exp_result.we_wordv[0][0] != '\0') { + ep.expanded = exp_result.we_wordv[0]; + ep.result = EXPAND_OK; + } else { + // Empty expansion + ep.expanded = ""; + ep.result = EXPAND_EMPTY; + } + + wordfree(&exp_result); + return ep; +} + +static std::string resolve_real_path(const std::string& path) +{ + if (path.empty()) { + return path; + } + + char resolved[PATH_MAX]; + if (realpath(path.c_str(), resolved) != nullptr) { + return std::string(resolved); + } + + // If realpath fails, try to handle relative paths manually + if (path[0] != '/') { + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd))) { + std::string abs = std::string(cwd) + "/" + path; + if (realpath(abs.c_str(), resolved) != nullptr) { + return std::string(resolved); + } + return abs; // Return constructed absolute path even if realpath fails + } + } + + return path; // Return original if we can't resolve +} + +static const MountPoint* find_mount_for_path(const std::string& resolved_path, + const std::vector& mounts) +{ + if (resolved_path.empty()) { + return nullptr; + } + + // Find longest matching mount point + for (const auto& mp : mounts) { + if (resolved_path == mp.path || + (resolved_path.find(mp.path + "/") == 0)) { + return ∓ + } + } + + return nullptr; +} + +struct PathAnalysis { + std::string original; + std::string expanded; + std::string resolved; + bool is_ok; + std::vector warnings; + const MountPoint* mount; + + PathAnalysis(const std::string& orig) : original(orig), is_ok(false), mount(nullptr) {} +}; + +static std::string find_writable_parent(const std::string& path) +{ + // Find the first existing parent directory + std::string test_path = path; + + while (!test_path.empty() && test_path != "/") { + if (access(test_path.c_str(), F_OK) == 0) { + // Path exists, check if writable + return test_path; + } + + // Move to parent + size_t last_slash = test_path.rfind('/'); + if (last_slash == std::string::npos) { + break; + } + test_path = test_path.substr(0, last_slash); + if (test_path.empty()) { + test_path = "/"; + } + } + + return "/"; +} + +static PathAnalysis analyze_path(const std::string& path, const std::vector& mounts, + bool check_for_python = false) +{ + PathAnalysis analysis(path); + + if (path.empty()) { + analysis.warnings.push_back("Path is empty"); + return analysis; + } + + // Expand variables + ExpandedPath ep = expand_path(path); + analysis.expanded = ep.expanded; + + if (ep.result == EXPAND_UNDEFINED_VAR) { + analysis.warnings.push_back("Variable is not set"); + return analysis; + } else if (ep.result == EXPAND_EMPTY) { + analysis.warnings.push_back("Variable expands to empty string"); + return analysis; + } else if (ep.result == EXPAND_ERROR) { + analysis.warnings.push_back("Cannot expand path"); + return analysis; + } + + // Check for empty segments (e.g., /var// from /var/$EMPTY/) + if (analysis.expanded.find("//") != std::string::npos) { + analysis.warnings.push_back("Path contains empty segment (variable may be empty)"); + } + + // Resolve to real path + analysis.resolved = resolve_real_path(ep.expanded); + + // Check for kernel filesystems first (these are always OK) + if (is_kernel_filesystem(analysis.resolved)) { + analysis.is_ok = true; + return analysis; + } + + // Check if path exists + bool path_exists = false; + struct stat st; + if (stat(analysis.resolved.c_str(), &st) == 0) { + path_exists = true; + } else { + // For non-python paths, check if parent is writable + if (!check_for_python) { + std::string parent = find_writable_parent(analysis.resolved); + bool parent_writable = (access(parent.c_str(), W_OK) == 0); + + if (parent_writable) { + analysis.warnings.push_back("Path does not exist (will be created by Spindle)"); + } else { + analysis.warnings.push_back("Path does not exist and parent directory is not writable"); + analysis.is_ok = false; + return analysis; + } + } else { + // Python prefix must exist + analysis.warnings.push_back("Path does not exist"); + return analysis; + } + } + + // Special validation for Python prefix + if (check_for_python && path_exists) { + // Check for bin/python or bin/python3 + std::string python_path = analysis.resolved + "/bin/python"; + std::string python3_path = analysis.resolved + "/bin/python3"; + + bool has_python = (access(python_path.c_str(), X_OK) == 0); + bool has_python3 = (access(python3_path.c_str(), X_OK) == 0); + + if (!has_python && !has_python3) { + analysis.warnings.push_back("No bin/python or bin/python3 found (not a Python installation?)"); + } + } + + // Find mount point (only for cache/comm paths, not python prefix) + if (!check_for_python) { + analysis.mount = find_mount_for_path(analysis.resolved, mounts); + + if (!analysis.mount) { + analysis.warnings.push_back("Cannot determine filesystem type"); + return analysis; + } + + if (analysis.mount->is_remote) { + analysis.warnings.push_back("Path is on REMOTE filesystem (" + analysis.mount->fstype + ")"); + return analysis; + } + } + + // All checks passed (or acceptable warnings) + if (analysis.warnings.empty()) { + analysis.is_ok = true; + } + + return analysis; +} + +static std::string get_timestamp() +{ + time_t now = time(nullptr); + char buf[80]; + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now)); + return std::string(buf); +} + +static bool test_directory_writable(const std::string& path) +{ + // Try to create a test file in the directory + std::string testfile = path + "/.spindle_config_test_" + std::to_string(getpid()); + + int fd = open(testfile.c_str(), O_CREAT | O_WRONLY | O_EXCL, 0600); + if (fd < 0) { + return false; + } + + close(fd); + unlink(testfile.c_str()); + return true; +} + +static bool validate_tmpdir_path(const std::string& path, + const std::vector& mounts, + std::string& error_msg) +{ + // Resolve the path (in case it's a symlink) + std::string resolved = resolve_real_path(path); + + // Check if path exists + struct stat st; + if (stat(resolved.c_str(), &st) != 0) { + error_msg = "Path does not exist: " + path; + if (resolved != path) { + error_msg += " (resolves to " + resolved + ")"; + } + return false; + } + + // Check if it's a directory + if (!S_ISDIR(st.st_mode)) { + error_msg = "Not a directory: " + path; + return false; + } + + // Check if writable + if (!test_directory_writable(resolved)) { + error_msg = "Directory is not writable: " + path; + return false; + } + + // Check if on local filesystem + const MountPoint* mp = find_mount_for_path(resolved, mounts); + if (!mp) { + error_msg = "Cannot determine filesystem type for: " + path; + return false; + } + + if (mp->is_remote) { + error_msg = "Path is on REMOTE filesystem (" + mp->fstype + "): " + path; + return false; + } + + return true; +} + +static std::string determine_best_path(const std::vector& mounts, + std::vector& warnings) +{ + // Priority order: TMPDIR, TEMPDIR, TMP, then /tmp + const char* env_vars[] = {"TMPDIR", "TEMPDIR", "TMP", nullptr}; + bool any_env_set = false; + std::string first_valid_path; + + // Check environment variables + for (int i = 0; env_vars[i] != nullptr; i++) { + const char* env_value = getenv(env_vars[i]); + + if (env_value && env_value[0] != '\0') { + any_env_set = true; + std::string tmpdir = env_value; + std::string error_msg; + + if (validate_tmpdir_path(tmpdir, mounts, error_msg)) { + if (first_valid_path.empty()) { + first_valid_path = tmpdir; + } + } + } + } + + // Generate appropriate WARNING or Note for TMPDIR section + if (!any_env_set) { + warnings.push_back("WARNING: The TMPDIR environment variable was not set."); + } else if (!first_valid_path.empty()) { + warnings.push_back("Note: The TMPDIR path is local and writeable."); + } else { + warnings.push_back("WARNING: The TMPDIR environment variable is not a local, writeable filesystem."); + } + + // Return best path found, or fall back to /tmp + if (!first_valid_path.empty()) { + return first_valid_path; + } + + // Fall back to /tmp + std::string error_msg; + if (validate_tmpdir_path("/tmp", mounts, error_msg)) { + return "/tmp"; + } + + // No suitable path found + return ""; +} + +// Get Python paths from sys.prefix and sys.path +static std::vector get_python_sys_paths() +{ + std::set paths; + + // Run Python with PYTHONHOME unset to get system defaults + // This ensures we get valid paths even if PYTHONHOME is broken + const char* cmd = "unset PYTHONHOME; python3 -c \"import sys; print(sys.prefix); print('\\n'.join(sys.path))\" 2>/dev/null"; + FILE* fp = popen(cmd, "r"); + if (!fp) { + return {}; + } + + char line[PATH_MAX]; + while (fgets(line, sizeof(line), fp) != nullptr) { + // Remove trailing newline + size_t len = strlen(line); + if (len > 0 && line[len-1] == '\n') { + line[len-1] = '\0'; + } + + if (strlen(line) > 0) { + // Check if this looks like a valid path + if (line[0] == '/' && access(line, F_OK) == 0) { + paths.insert(line); + } + } + } + pclose(fp); + + return std::vector(paths.begin(), paths.end()); +} + +// Deduplicate paths by keeping only top-level directories +// If /foo is in the list and /foo/bar is also in the list, keep only /foo +static std::vector deduplicate_paths(const std::vector& paths) +{ + if (paths.empty()) return paths; + + // Sort by path length (shortest first) + std::vector sorted_paths = paths; + std::sort(sorted_paths.begin(), sorted_paths.end(), + [](const std::string& a, const std::string& b) { + return a.length() < b.length(); + }); + + std::vector result; + for (const auto& path : sorted_paths) { + bool is_subpath = false; + + // Check if this path is under any already-kept path + for (const auto& kept : result) { + // Check if path starts with kept/ + if (path.size() > kept.size() && + path.compare(0, kept.size(), kept) == 0 && + (path[kept.size()] == '/' || kept == "/")) { + is_subpath = true; + break; + } + } + + if (!is_subpath) { + result.push_back(path); + } + } + + return result; +} + +// Check if a path contains Python libraries +static bool is_valid_python_prefix(const std::string& path) +{ + // Check for lib/python* or lib64/python* directories + std::string lib_path = path + "/lib"; + std::string lib64_path = path + "/lib64"; + + DIR* dir = opendir(lib_path.c_str()); + if (dir) { + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + if (strncmp(entry->d_name, "python", 6) == 0) { + closedir(dir); + return true; + } + } + closedir(dir); + } + + dir = opendir(lib64_path.c_str()); + if (dir) { + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + if (strncmp(entry->d_name, "python", 6) == 0) { + closedir(dir); + return true; + } + } + closedir(dir); + } + + return false; +} + +static std::vector discover_python_prefixes() +{ + std::set prefixes; + + // Priority 1: Check PYTHONHOME environment variable and validate paths + const char* pythonhome_env = getenv("PYTHONHOME"); + if (pythonhome_env && pythonhome_env[0] != '\0') { + // Parse colon-separated list + std::string paths_str(pythonhome_env); + size_t start = 0; + size_t end = paths_str.find(':'); + + while (end != std::string::npos) { + std::string path = paths_str.substr(start, end - start); + if (!path.empty() && is_valid_python_prefix(path)) { + prefixes.insert(path); + } + start = end + 1; + end = paths_str.find(':', start); + } + + // Add last path + std::string path = paths_str.substr(start); + if (!path.empty() && is_valid_python_prefix(path)) { + prefixes.insert(path); + } + + // If PYTHONHOME provided valid Python installations, use only those + if (!prefixes.empty()) { + std::vector all_paths(prefixes.begin(), prefixes.end()); + return deduplicate_paths(all_paths); + } + } + + // Priority 2: Fall back to Python sys.prefix (if PYTHONHOME not set or had no valid paths) + std::vector sys_paths = get_python_sys_paths(); + for (const auto& path : sys_paths) { + prefixes.insert(path); + } + + std::vector all_paths(prefixes.begin(), prefixes.end()); + return deduplicate_paths(all_paths); +} + +static bool has_remote_mount_under(const std::string& prefix, const std::vector& mounts) +{ + // Check if any remote mount exists under this prefix + std::string prefix_slash = prefix; + if (prefix != "/" && prefix_slash.back() != '/') { + prefix_slash += "/"; + } + + for (const auto& mp : mounts) { + if (!mp.is_remote) continue; + + // Check if this remote mount is under prefix + if (mp.path == prefix || mp.path.find(prefix_slash) == 0) { + return true; + } + } + return false; +} + +static std::vector generate_localprefix(const std::vector& mounts) +{ + std::vector local_dirs; + + // Always include kernel filesystems + local_dirs.push_back("/proc"); + local_dirs.push_back("/sys"); + local_dirs.push_back("/dev"); + + // Get top-level directories + DIR* dir = opendir("/"); + if (!dir) { + return local_dirs; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + if (entry->d_name[0] == '.') continue; + + std::string top_level = std::string("/") + entry->d_name; + + // Skip if already added (kernel filesystems) + if (top_level == "/proc" || top_level == "/sys" || top_level == "/dev") { + continue; + } + + // Check if directory + struct stat st; + if (stat(top_level.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) { + continue; + } + + // Skip if has remote mounts underneath + if (has_remote_mount_under(top_level, mounts)) { + continue; + } + + local_dirs.push_back(top_level); + } + closedir(dir); + + // Sort for consistent output (kernel filesystems first) + std::sort(local_dirs.begin() + 3, local_dirs.end()); + + return local_dirs; +} + +static std::string get_hostname_for_ssh() +{ + char hostname[256]; + if (gethostname(hostname, sizeof(hostname)) != 0) { + return ""; + } + return std::string(hostname); +} + +// Forward declaration +static bool test_ssh_connectivity(const std::string& hostname, std::string& error_msg); + +// Structure to hold SSH test results for MPI communication +struct SSHTestResult { + int rank; + int target_rank; + bool success; + char error_msg[256]; + char source_host[256]; + char target_host[256]; +}; + +// Ring topology SSH test: each rank tests SSH to next rank +static void test_ssh_ring(int mpi_rank, int mpi_size, std::vector& warnings) +{ + // Get local hostname + std::string local_hostname = get_hostname_for_ssh(); + + // Exchange hostnames among all ranks using MPI_Allgather + const int MAX_HOSTNAME_LEN = 256; + char local_host[MAX_HOSTNAME_LEN]; + char all_hosts[MAX_HOSTNAME_LEN * mpi_size]; + + strncpy(local_host, local_hostname.c_str(), MAX_HOSTNAME_LEN - 1); + local_host[MAX_HOSTNAME_LEN - 1] = '\0'; + + MPI_Allgather(local_host, MAX_HOSTNAME_LEN, MPI_CHAR, + all_hosts, MAX_HOSTNAME_LEN, MPI_CHAR, + MPI_COMM_WORLD); + + // Determine target: (rank + 1) % size + int target_rank = (mpi_rank + 1) % mpi_size; + std::string target_hostname(&all_hosts[target_rank * MAX_HOSTNAME_LEN]); + + // Test SSH to target + SSHTestResult result; + result.rank = mpi_rank; + result.target_rank = target_rank; + strncpy(result.source_host, local_hostname.c_str(), 255); + result.source_host[255] = '\0'; + strncpy(result.target_host, target_hostname.c_str(), 255); + result.target_host[255] = '\0'; + + std::string ssh_error; + result.success = test_ssh_connectivity(target_hostname, ssh_error); + strncpy(result.error_msg, ssh_error.c_str(), 255); + result.error_msg[255] = '\0'; + + // Gather all results to rank 0 + SSHTestResult all_results[mpi_size]; + MPI_Gather(&result, sizeof(SSHTestResult), MPI_BYTE, + all_results, sizeof(SSHTestResult), MPI_BYTE, + 0, MPI_COMM_WORLD); + + // Rank 0 processes and reports results + if (mpi_rank == 0) { + int successful_tests = 0; + bool all_passed = true; + + for (int i = 0; i < mpi_size; i++) { + if (all_results[i].success) { + successful_tests++; + } else { + all_passed = false; + } + } + + // Count unique nodes from the hostname list + std::set unique_nodes; + for (int i = 0; i < mpi_size; i++) { + std::string hostname(&all_hosts[i * MAX_HOSTNAME_LEN]); + if (!hostname.empty()) { + unique_nodes.insert(hostname); + } + } + int num_nodes = unique_nodes.size(); + + if (all_passed) { + char msg[512]; + snprintf(msg, sizeof(msg), "Note: Tested ssh successfully under MPI using %d ranks across %d nodes.", + mpi_size, num_nodes); + warnings.push_back(std::string(msg)); + } else { + warnings.push_back("WARNING: Executing under MPI, but ssh communication among compute nodes failed."); + + // Report detailed failures as additional lines + char msg[512]; + for (int i = 0; i < mpi_size; i++) { + if (!all_results[i].success) { + snprintf(msg, sizeof(msg), "# Rank %d (%s) -> Rank %d (%s): FAILED - %s", + all_results[i].rank, all_results[i].source_host, + all_results[i].target_rank, all_results[i].target_host, + all_results[i].error_msg); + warnings.push_back(std::string(msg)); + } + } + } + } +} + +static bool test_ssh_connectivity(const std::string& hostname, std::string& error_msg) +{ + if (hostname.empty()) { + error_msg = "Could not determine hostname"; + return false; + } + + // Build SSH command: ssh -o BatchMode=yes -o ConnectTimeout=10 /bin/true + std::string cmd = "ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=no " + + hostname + " /bin/true 2>&1"; + + FILE* fp = popen(cmd.c_str(), "r"); + if (!fp) { + error_msg = "Failed to execute ssh command"; + return false; + } + + // Read any error output + char buffer[256]; + std::string output; + while (fgets(buffer, sizeof(buffer), fp) != nullptr) { + output += buffer; + } + + int status = pclose(fp); + int exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1; + + if (exit_code == 0) { + return true; + } + + // SSH failed - extract useful error info + if (output.find("Permission denied") != std::string::npos) { + error_msg = "SSH authentication failed (check SSH keys)"; + } else if (output.find("Connection refused") != std::string::npos) { + error_msg = "SSH connection refused"; + } else if (output.find("Connection timed out") != std::string::npos) { + error_msg = "SSH connection timed out"; + } else if (output.find("No route to host") != std::string::npos) { + error_msg = "No route to host"; + } else if (!output.empty()) { + // Truncate long error messages + error_msg = output.substr(0, 100); + if (output.length() > 100) error_msg += "..."; + } else { + error_msg = "SSH test failed (exit code " + std::to_string(exit_code) + ")"; + } + + return false; +} + +// Forward declarations +static void print_config_header(std::vector& warnings); + +// Parse a spindle.conf file and return key-value pairs +// Returns: map of key -> value pairs +// Ignores comments and blank lines +static void print_config_header(std::vector& warnings) +{ + std::cout << "# spindle.conf generated by spindle-config-helper version " + << PACKAGE_VERSION << std::endl; + std::cout << "# Generated: " << get_timestamp() << std::endl; + std::cout << "#" << std::endl; + + // Print each warning/note on its own line (already prefixed with # WARNING: or # Note:) + for (const auto& warning : warnings) { + std::cout << "# " << warning << std::endl; + } + + if (!warnings.empty()) { + std::cout << "#" << std::endl; + } +} + +static void print_path_variable(const std::string& name, const std::string& value, + const std::vector& comments) +{ + // Print comments above the variable + for (const auto& comment : comments) { + std::cout << "# " << comment << std::endl; + } + + // If value is empty, comment out the line + if (value.empty()) { + std::cout << "# " << name << " =" << std::endl; + } else { + std::cout << name << " = " << value << std::endl; + } + // No blank line after - we put blank lines BEFORE sections instead +} + +static void generate_default_config(const ConfigMap& confmap, + const std::vector& mounts, + std::vector& header_warnings, + int mpi_rank, int mpi_size) +{ + // Determine best path for commpaths/cachepaths if using defaults + std::string recommended_path; + bool need_path_recommendation = false; + + // Check if commpaths or cachepaths are using the default $TMPDIR + auto commpaths_result = confmap.getValueString(confCommPaths); + auto cachepaths_result = confmap.getValueString(confCachePaths); + + bool commpaths_is_default = (commpaths_result.first && commpaths_result.second == "$TMPDIR"); + bool cachepaths_is_default = (cachepaths_result.first && cachepaths_result.second == "$TMPDIR"); + + if (commpaths_is_default || cachepaths_is_default) { + need_path_recommendation = true; + std::vector path_warnings; + recommended_path = determine_best_path(mounts, path_warnings); + + // Add path-related warnings to header + for (const auto& warning : path_warnings) { + header_warnings.push_back(warning); + } + + if (recommended_path.empty()) { + header_warnings.push_back("CRITICAL: No suitable local filesystem found for commpaths/cachepaths"); + header_warnings.push_back(" Cannot generate valid configuration"); + // Print header with errors and exit + print_config_header(header_warnings); + return; + } + } + + // SSH test results already collected in header_warnings (done in main before calling this function) + + // Header + print_config_header(header_warnings); + + // Generate the 4 path-based variables with analysis + auto output_path_option = [&](SpindleConfigID config_id, const char* name, + bool check_python = false) { + // Check if this option is set (either default or user-specified) + if (!confmap.isSet(config_id)) { + return; + } + + auto result = confmap.getValueString(config_id); + if (!result.first || result.second.empty()) { + return; + } + + std::string paths = result.second; + std::vector comments; + + // If this is commpaths or cachepaths with $TMPDIR default, use recommended path + if (need_path_recommendation && + (config_id == confCommPaths || config_id == confCachePaths) && + paths == "$TMPDIR" && !recommended_path.empty()) { + paths = recommended_path; + } + + // For pythonprefix, discover additional Python installations + std::vector discovered_pythons; + if (config_id == confPythonPrefix) { + discovered_pythons = discover_python_prefixes(); + } + + // Analyze each path in the colon-separated list + std::vector path_list; + std::vector analyses; + size_t pos = 0; + while (pos < paths.length()) { + size_t end = paths.find(':', pos); + if (end == std::string::npos) { + end = paths.length(); + } + std::string path = paths.substr(pos, end - pos); + if (!path.empty()) { + path_list.push_back(path); + analyses.push_back(analyze_path(path, mounts, check_python)); + } + pos = end + 1; + } + + // Add discovered Python paths to the list + if (config_id == confPythonPrefix) { + for (const auto& pypath : discovered_pythons) { + // Check if not already in list + bool already_present = false; + for (const auto& existing : path_list) { + PathAnalysis ex = analyze_path(existing, mounts, check_python); + if (ex.resolved == resolve_real_path(pypath)) { + already_present = true; + break; + } + } + if (!already_present) { + path_list.push_back(pypath); + analyses.push_back(analyze_path(pypath, mounts, check_python)); + comments.push_back("NOTE: Added " + pypath + " from 'which python/python3'"); + } + } + } + + // Separate usable from unusable paths + std::vector good_paths, bad_paths; + + for (size_t i = 0; i < path_list.size(); i++) { + bool has_critical_issues = false; + + // For pythonprefix, check for Python installation issues + if (check_python) { + for (const auto& warning : analyses[i].warnings) { + if (warning.find("No bin/python") != std::string::npos || + warning.find("Path does not exist") != std::string::npos || + warning.find("parent directory is not writable") != std::string::npos) { + has_critical_issues = true; + break; + } + } + } else { + // For commpaths/cachepaths/localprefix + for (const auto& warning : analyses[i].warnings) { + if (warning.find("parent directory is not writable") != std::string::npos || + warning.find("REMOTE filesystem") != std::string::npos) { + has_critical_issues = true; + break; + } + } + } + + if (has_critical_issues) { + bad_paths.push_back(path_list[i]); + } else { + good_paths.push_back(path_list[i]); + } + } + + // If no good paths, add fallback + if (good_paths.empty() && (config_id == confCommPaths || config_id == confCachePaths)) { + if (!recommended_path.empty()) { + good_paths.push_back(recommended_path); + comments.push_back("NOTE: No usable paths found, using " + recommended_path); + } + } + + // Silently drop bad paths - no need to warn about them + + // Use only good paths in output + if (!good_paths.empty()) { + std::string filtered; + for (const auto& p : good_paths) { + if (!filtered.empty()) filtered += ":"; + filtered += p; + } + paths = filtered; + } else { + // No good paths at all - output commented line + paths = ""; + if (check_python) { + comments.push_back("WARNING: No valid Python installations found"); + } + } + + // Add comment for usable paths if no issues + bool has_issues = !bad_paths.empty(); + if (!has_issues && !good_paths.empty()) { + PathAnalysis first = analyze_path(good_paths[0], mounts, check_python); + if (first.mount && !first.mount->is_remote) { + comments.push_back(std::string(name) + ": Local " + first.mount->fstype + + " filesystem, " + std::to_string(first.mount->avail_gb) + "GB available"); + } else if (is_kernel_filesystem(first.resolved)) { + comments.push_back(std::string(name) + ": Kernel filesystem"); + } + } + + print_path_variable(name, paths, comments); + }; + + // Add comment block for commpaths and cachepaths + std::cout << "#" << std::endl; + std::cout << "# commpaths and cachepaths" << std::endl; + std::cout << "# These two settings are colon-separated lists of local (on-node) storage that can be used for Spindle" << std::endl; + std::cout << "# sockets and caches, respectively. If the entries below are commented out, they either pointed to" << std::endl; + std::cout << "# a non-local filesystem or were not writeable by this user. Both of these are based on the" << std::endl; + std::cout << "# TMPDIR environment variable; fallback is /tmp." << std::endl; + output_path_option(confCommPaths, "commpaths"); + output_path_option(confCachePaths, "cachepaths"); + + // Add comment block for python-prefix + std::cout << "#" << std::endl; + std::cout << "# python-prefix" << std::endl; + std::cout << "# This is a colon-separated list of paths that contain python packages (including site-specific" << std::endl; + std::cout << "# packages). The directories will include PYTHONHOME (if specified) and the results of" << std::endl; + std::cout << "# checking python's sys.prefix and sys.path. If the entry is commented out, the program could" << std::endl; + std::cout << "# not locate python libraries." << std::endl; + + output_path_option(confPythonPrefix, "pythonprefix", true); + + // Special handling for localprefix - generate from top-level dirs without remote mounts + auto localprefix_result = confmap.getValueString(confLocalPrefix); + if (localprefix_result.first) { + std::vector generated_locals = generate_localprefix(mounts); + std::vector comments; + + // Check if user specified additional paths (beyond defaults) + std::string user_value = localprefix_result.second; + std::set user_paths; + + // Parse user's colon-separated list + size_t pos = 0; + while (pos < user_value.length()) { + size_t end = user_value.find(':', pos); + if (end == std::string::npos) end = user_value.length(); + + std::string path = user_value.substr(pos, end - pos); + // Strip trailing slash for comparison + if (!path.empty() && path.back() == '/') path.pop_back(); + if (!path.empty()) user_paths.insert(path); + + pos = end + 1; + } + + // Add generated paths to user paths + std::set all_paths(user_paths); + for (const auto& gpath : generated_locals) { + all_paths.insert(gpath); + } + + // Validate user additions + for (const auto& upath : user_paths) { + // Check if this was NOT auto-generated (meaning user added it) + bool was_generated = (std::find(generated_locals.begin(), generated_locals.end(), upath) != generated_locals.end()); + if (!was_generated) { + // User added this - validate it + if (has_remote_mount_under(upath, mounts)) { + comments.push_back("WARNING: User-specified path has remote mounts underneath: " + upath); + } else { + comments.push_back("NOTE: User added: " + upath); + } + } + } + + // Build output with trailing slashes + std::string output_value; + for (const auto& dir : all_paths) { + if (!output_value.empty()) output_value += ":"; + output_value += (dir == "/" ? "/" : dir + "/"); + } + + // Add comment block for localprefix + std::cout << "#" << std::endl; + std::cout << "# localprefix" << std::endl; + std::cout << "# This is a colon-separated list of paths that Spindle should treat as node-local filesystems" << std::endl; + std::cout << "# (i.e., Spindle will not attempt to cache anything underneath these directories). Overlay" << std::endl; + std::cout << "# filesystems are assumed to be non-local." << std::endl; + + print_path_variable("localprefix", output_value, comments); + } +} + +static void print_version() +{ + std::cout << "spindle-config-helper version " << PACKAGE_VERSION << std::endl; +} + +static bool check_munge_available(bool in_sandbox) +{ + // Check if munge binary exists + if (access("/usr/bin/munge", X_OK) != 0) { + return false; + } + + // If in sandbox, we can't test the daemon + if (in_sandbox) { + return true; // Binary exists, can't test further + } + + // Try to actually use munge to verify daemon is running + pid_t pid = fork(); + if (pid == 0) { + // Child: run munge -n with stdout/stderr redirected to /dev/null + int devnull = open("/dev/null", O_WRONLY); + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + close(devnull); + execl("/usr/bin/munge", "munge", "-n", (char*)NULL); + _exit(127); + } else if (pid > 0) { + // Parent: wait for child + int status; + waitpid(pid, &status, 0); + return WIFEXITED(status) && WEXITSTATUS(status) == 0; + } + + return false; // fork failed +} + +static void print_usage(const char *progname) +{ + std::cout << "Usage: " << progname << " [OPTIONS] [-- SPINDLE_OPTIONS]\n\n" + << "Generate and validate Spindle configuration files for HPC environments.\n\n" + << "INSTRUCTIONS:\n" + << " 1) Set PYTHONPREFIX to the python your programs will use.\n" + << " 2) Set TMPDIR to node-local temporary storage (e.g., /tmp).\n" + << " 3) Run " << progname << " as you would any other parallel\n" + << " program, e.g., flux run -N2 -n2 " << progname << ".\n" + << " 4) Check the output, then and redirect/copypaste to\n" + << " ~/.spindle/spindle.conf\n\n" + << "See the output for explanations of how spindle-config-helper chooses\n" + << " paths and validates the environment.\n\n" + << "OPTIONS:\n" + << " -h, --help Display this help message and exit\n" + << " -V, --version Display version information and exit\n\n" + << "SEE ALSO:\n" + << " spindle(1), spindle-config-helper(1)\n\n" + << "VERSION:\n" + << " spindle-config-helper " << PACKAGE_VERSION << "\n" + << std::endl; +} + +// Detect if we're running under an MPI launcher by checking for MPI-related environment variables +static bool running_under_mpi() +{ + // Check for common MPI launcher environment variables + const char* mpi_env_vars[] = { + "PMI_RANK", // PMI-based launchers + "PMI_SIZE", + "PMIX_RANK", // PMIx + "SLURM_PROCID", // Slurm + "SLURM_NTASKS", + "FLUX_TASK_RANK", // Flux + "FLUX_JOB_SIZE", + "OMPI_COMM_WORLD_RANK", // OpenMPI + "MPI_LOCALRANKID", // Intel MPI + nullptr + }; + + for (int i = 0; mpi_env_vars[i] != nullptr; i++) { + if (getenv(mpi_env_vars[i]) != nullptr) { + return true; + } + } + + return false; +} + +int main(int argc, char *argv[]) +{ + // Initialize MPI if running under an MPI launcher + int mpi_rank = 0, mpi_size = 1; + bool is_mpi_job = running_under_mpi(); + int mpi_initialized = 0; + + if (is_mpi_job) { + MPI_Initialized(&mpi_initialized); + + if (!mpi_initialized) { + MPI_Init(&argc, &argv); + } + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); + MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); + } + + // Handle -h/--help and -V/--version before config parsing + // Also check for -- --usage which should show Spindle's usage + bool show_spindle_usage = false; + int separator_idx = -1; + // First pass: check for help/version and find separator + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--") == 0) { + separator_idx = i; + continue; + } + + if (separator_idx == -1) { + // Before --: check for config-helper options + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + if (mpi_rank == 0) { + print_usage(argv[0]); + } + if (is_mpi_job) { + MPI_Finalize(); + } + return 0; + } + if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) { + if (mpi_rank == 0) { + print_version(); + } + if (is_mpi_job) { + MPI_Finalize(); + } + return 0; + } + if (strcmp(argv[i], "--usage") == 0) { + show_spindle_usage = true; + } + } else { + // After --: check for --usage + if (strcmp(argv[i], "--usage") == 0) { + show_spindle_usage = true; + } + } + } + + // Handle -- --usage early (don't generate config) + if (show_spindle_usage) { + // Pass through to Spindle's option system by continuing with normal flow + // but we'll build argv with --usage and let it print + // Actually, just build minimal argv and call gatherAllConfigInfo + // which will handle --usage and exit + + std::vector usage_argv; + usage_argv.push_back(argv[0]); + + // Add --usage flag + char *usage_flag = strdup("--usage"); + usage_argv.push_back(usage_flag); + + // Add options after -- (if any) + if (separator_idx != -1) { + for (int i = separator_idx + 1; i < argc; i++) { + // Skip --usage if it appears here too + if (strcmp(argv[i], "--usage") != 0) { + usage_argv.push_back(argv[i]); + } + } + } + + // Add dummy app + char *dummy_sep = strdup("--"); + char *dummy_app = strdup("/bin/true"); + usage_argv.push_back(dummy_sep); + usage_argv.push_back(dummy_app); + + char **usage_argv_ptr = usage_argv.data(); + int usage_argc = usage_argv.size(); + + ConfigMap confmap; + std::string errmsg; + gatherAllConfigInfo(usage_argc, usage_argv_ptr, false, confmap, errmsg); + + free(usage_flag); + free(dummy_sep); + free(dummy_app); + if (is_mpi_job) { + MPI_Finalize(); + } + return 0; + } + + // Reconstruct argv to move Spindle options before -- and add dummy app + // Input: spindle-config-helper [config-helper-opts] -- [spindle-opts] + // Output: spindle-config-helper [spindle-opts] -- /bin/true (filtering out config-helper opts) + + std::vector new_argv; + new_argv.push_back(argv[0]); // Program name + + // No config-helper options to filter anymore (only -h/-V which exit early) + // Just copy all args, respecting the -- separator + + if (separator_idx != -1) { + // Add args before -- (should be nothing since -h/-V exit early) + // And add spindle options (after --) + for (int i = separator_idx + 1; i < argc; i++) { + new_argv.push_back(argv[i]); + } + } + // If no --, nothing to add (no spindle options) + + // Add dummy app + char *dummy_sep = strdup("--"); + char *dummy_app = strdup("/bin/true"); + new_argv.push_back(dummy_sep); + new_argv.push_back(dummy_app); + + char **modified_argv = new_argv.data(); + int modified_argc = new_argv.size(); + + ConfigMap confmap; + std::string errmsg; + + // Reuse Spindle's existing parsing: cmdline, env vars, config files + // Pass false for inExecutable since we're not the spindle launcher + if (!gatherAllConfigInfo(modified_argc, modified_argv, false, confmap, errmsg)) { + std::cerr << "Error: " << errmsg << std::endl; + free(dummy_app); + free(dummy_sep); + return 1; + } + + // Clean up temporary argv + free(dummy_app); + free(dummy_sep); + + // Parse and analyze mount points (all ranks need this for SSH test) + std::vector mounts = parse_mounts(); + + // Collect header warnings + std::vector header_warnings; + + // Generate PYTHONHOME warning/note (only rank 0 needs to do this since it's the only one printing) + if (mpi_rank == 0) { + const char* pythonhome_env = getenv("PYTHONHOME"); + if (!pythonhome_env || pythonhome_env[0] == '\0') { + header_warnings.push_back("WARNING: The PYTHONHOME environment variable was not set."); + } else { + // Check if paths are valid + std::vector py_paths = discover_python_prefixes(); + if (py_paths.empty()) { + header_warnings.push_back("WARNING: None of the PYTHONHOME paths are valid."); + } else { + header_warnings.push_back("Note: At least one path from PYTHONHOME is valid and contains Python packages."); + } + } + } + + // Generate Munge warning/note (only rank 0) + if (mpi_rank == 0) { + const char* sandbox_engine = getenv("SANDBOX_ENGINE"); + bool in_sandbox = (sandbox_engine != nullptr); + + if (check_munge_available(in_sandbox)) { + header_warnings.push_back("Note: Munge is working as expected."); + } else { + header_warnings.push_back("WARNING: Munge is either not set up or not supported."); + } + } + + // ALL RANKS: Test SSH connectivity using ring topology (uses collective MPI operations) + // MPI/SSH warning will be generated by test_ssh_ring or afterward + if (!is_mpi_job) { + // Not running under MPI + if (mpi_rank == 0) { + header_warnings.push_back("WARNING: Not executing under MPI. This environment may not match what is used by parallel programs."); + } + } else if (mpi_size > 1) { + test_ssh_ring(mpi_rank, mpi_size, header_warnings); + } else if (mpi_rank == 0) { + // Single node - add single node warning + header_warnings.push_back("WARNING: Executing under MPI with a single compute node. This may not give accurate ssh results."); + } + + // Only rank 0 generates and prints output + if (mpi_rank == 0) { + // Generate config to stdout + generate_default_config(confmap, mounts, header_warnings, mpi_rank, mpi_size); + } + + if (is_mpi_job) { + MPI_Finalize(); + } + return 0; +} diff --git a/src/fe/startup/Makefile.am b/src/fe/startup/Makefile.am index c4daceb3..82582e89 100644 --- a/src/fe/startup/Makefile.am +++ b/src/fe/startup/Makefile.am @@ -1,6 +1,7 @@ bin_PROGRAMS = spindle lib_LTLIBRARIES = libspindlefe.la noinst_LTLIBRARIES = libspindlefe_static.la +noinst_LIBRARIES = libconfigparse.a include_HEADERS = $(top_srcdir)/../include/spindle_launch.h @@ -43,3 +44,10 @@ if LMON spindle_LDADD += $(top_builddir)/launchmon/libfelmon.la endif +# Private static library for config parsing (used by spindle-config-helper) +libconfigparse_a_SOURCES = config_mgr.cc config_parser.cc parseargs.cc \ + $(top_srcdir)/../utils/pathfn.c \ + $(top_srcdir)/../utils/keyfile.c \ + $(top_srcdir)/../utils/parseloc.c +libconfigparse_a_CPPFLAGS = $(CORE_CPPFLAGS) + diff --git a/src/fe/startup/Makefile.in b/src/fe/startup/Makefile.in index 0f678858..9ed31186 100644 --- a/src/fe/startup/Makefile.in +++ b/src/fe/startup/Makefile.in @@ -16,6 +16,7 @@ + VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ @@ -121,6 +122,7 @@ CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \ "$(DESTDIR)$(includedir)" PROGRAMS = $(bin_PROGRAMS) +LIBRARIES = $(noinst_LIBRARIES) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -149,12 +151,26 @@ am__uninstall_files_from_dir = { \ $(am__cd) "$$dir" && rm -f $$files; }; \ } LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libconfigparse_a_AR = $(AR) $(ARFLAGS) +libconfigparse_a_LIBADD = +am__dirstamp = $(am__leading_dot)dirstamp +am_libconfigparse_a_OBJECTS = libconfigparse_a-config_mgr.$(OBJEXT) \ + libconfigparse_a-config_parser.$(OBJEXT) \ + libconfigparse_a-parseargs.$(OBJEXT) \ + $(top_builddir)/../utils/libconfigparse_a-pathfn.$(OBJEXT) \ + $(top_builddir)/../utils/libconfigparse_a-keyfile.$(OBJEXT) \ + $(top_builddir)/../utils/libconfigparse_a-parseloc.$(OBJEXT) +libconfigparse_a_OBJECTS = $(am_libconfigparse_a_OBJECTS) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(top_builddir)/logging/libspindleflogc.la \ $(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) libspindlefe_la_DEPENDENCIES = $(am__DEPENDENCIES_2) -am__dirstamp = $(am__leading_dot)dirstamp am__objects_1 = libspindlefe_la-spindle_fe.lo \ libspindlefe_la-parseargs.lo libspindlefe_la-config_parser.lo \ libspindlefe_la-config_mgr.lo libspindlefe_la-parse_preload.lo \ @@ -235,7 +251,10 @@ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/../../scripts/depcomp am__maybe_remake_depfiles = depfiles -am__depfiles_remade = $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-keyfile.Plo \ +am__depfiles_remade = $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Po \ + $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Po \ + $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Po \ + $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-keyfile.Plo \ $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-parseloc.Plo \ $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-pathfn.Plo \ $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-rshlaunch.Plo \ @@ -247,6 +266,9 @@ am__depfiles_remade = $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-keyfile $(top_builddir)/../utils/$(DEPDIR)/spindle-parseloc.Po \ $(top_builddir)/../utils/$(DEPDIR)/spindle-pathfn.Po \ $(top_builddir)/../utils/$(DEPDIR)/spindle-rshlaunch.Po \ + ./$(DEPDIR)/libconfigparse_a-config_mgr.Po \ + ./$(DEPDIR)/libconfigparse_a-config_parser.Po \ + ./$(DEPDIR)/libconfigparse_a-parseargs.Po \ ./$(DEPDIR)/libspindlefe_la-config_mgr.Plo \ ./$(DEPDIR)/libspindlefe_la-config_parser.Plo \ ./$(DEPDIR)/libspindlefe_la-parse_preload.Plo \ @@ -308,9 +330,9 @@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = -SOURCES = $(libspindlefe_la_SOURCES) $(libspindlefe_static_la_SOURCES) \ - $(spindle_SOURCES) -DIST_SOURCES = $(libspindlefe_la_SOURCES) \ +SOURCES = $(libconfigparse_a_SOURCES) $(libspindlefe_la_SOURCES) \ + $(libspindlefe_static_la_SOURCES) $(spindle_SOURCES) +DIST_SOURCES = $(libconfigparse_a_SOURCES) $(libspindlefe_la_SOURCES) \ $(libspindlefe_static_la_SOURCES) $(am__spindle_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ @@ -491,6 +513,7 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ lib_LTLIBRARIES = libspindlefe.la noinst_LTLIBRARIES = libspindlefe_static.la +noinst_LIBRARIES = libconfigparse.a include_HEADERS = $(top_srcdir)/../include/spindle_launch.h AM_CPPFLAGS = -I$(top_srcdir)/../logging CORE_SOURCES = spindle_fe.cc parseargs.cc config_parser.cc config_mgr.cc parse_preload.cc $(top_srcdir)/../utils/pathfn.c $(top_srcdir)/../utils/keyfile.c $(top_srcdir)/../utils/parseloc.c $(top_srcdir)/../utils/rshlaunch.c @@ -515,6 +538,14 @@ spindle_LDADD = $(CORE_LDADD) \ $(top_builddir)/hostbin/libhostbin.la $(am__append_3) \ $(am__append_6) @BLD_FLUXPLUGIN_TRUE@spindle_LDFLAGS = $(fluxplugin-ldflags) -Wl,--allow-multiple-definition + +# Private static library for config parsing (used by spindle-config-helper) +libconfigparse_a_SOURCES = config_mgr.cc config_parser.cc parseargs.cc \ + $(top_srcdir)/../utils/pathfn.c \ + $(top_srcdir)/../utils/keyfile.c \ + $(top_srcdir)/../utils/parseloc.c + +libconfigparse_a_CPPFLAGS = $(CORE_CPPFLAGS) all: all-am .SUFFIXES: @@ -598,6 +629,9 @@ clean-binPROGRAMS: echo " rm -f" $$list; \ rm -f $$list +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ @@ -649,6 +683,20 @@ $(top_builddir)/../utils/$(am__dirstamp): $(top_builddir)/../utils/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) $(top_builddir)/../utils/$(DEPDIR) @: > $(top_builddir)/../utils/$(DEPDIR)/$(am__dirstamp) +$(top_builddir)/../utils/libconfigparse_a-pathfn.$(OBJEXT): \ + $(top_builddir)/../utils/$(am__dirstamp) \ + $(top_builddir)/../utils/$(DEPDIR)/$(am__dirstamp) +$(top_builddir)/../utils/libconfigparse_a-keyfile.$(OBJEXT): \ + $(top_builddir)/../utils/$(am__dirstamp) \ + $(top_builddir)/../utils/$(DEPDIR)/$(am__dirstamp) +$(top_builddir)/../utils/libconfigparse_a-parseloc.$(OBJEXT): \ + $(top_builddir)/../utils/$(am__dirstamp) \ + $(top_builddir)/../utils/$(DEPDIR)/$(am__dirstamp) + +libconfigparse.a: $(libconfigparse_a_OBJECTS) $(libconfigparse_a_DEPENDENCIES) $(EXTRA_libconfigparse_a_DEPENDENCIES) + $(AM_V_at)-rm -f libconfigparse.a + $(AM_V_AR)$(libconfigparse_a_AR) libconfigparse.a $(libconfigparse_a_OBJECTS) $(libconfigparse_a_LIBADD) + $(AM_V_at)$(RANLIB) libconfigparse.a $(top_builddir)/../utils/libspindlefe_la-pathfn.lo: \ $(top_builddir)/../utils/$(am__dirstamp) \ $(top_builddir)/../utils/$(DEPDIR)/$(am__dirstamp) @@ -704,6 +752,9 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-keyfile.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-parseloc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-pathfn.Plo@am__quote@ # am--include-marker @@ -716,6 +767,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/../utils/$(DEPDIR)/spindle-parseloc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/../utils/$(DEPDIR)/spindle-pathfn.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/../utils/$(DEPDIR)/spindle-rshlaunch.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libconfigparse_a-config_mgr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libconfigparse_a-config_parser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libconfigparse_a-parseargs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libspindlefe_la-config_mgr.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libspindlefe_la-config_parser.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libspindlefe_la-parse_preload.Plo@am__quote@ # am--include-marker @@ -771,6 +825,48 @@ am--depfiles: $(am__depfiles_remade) @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< +$(top_builddir)/../utils/libconfigparse_a-pathfn.o: $(top_builddir)/../utils/pathfn.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT $(top_builddir)/../utils/libconfigparse_a-pathfn.o -MD -MP -MF $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Tpo -c -o $(top_builddir)/../utils/libconfigparse_a-pathfn.o `test -f '$(top_builddir)/../utils/pathfn.c' || echo '$(srcdir)/'`$(top_builddir)/../utils/pathfn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Tpo $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/../utils/pathfn.c' object='$(top_builddir)/../utils/libconfigparse_a-pathfn.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o $(top_builddir)/../utils/libconfigparse_a-pathfn.o `test -f '$(top_builddir)/../utils/pathfn.c' || echo '$(srcdir)/'`$(top_builddir)/../utils/pathfn.c + +$(top_builddir)/../utils/libconfigparse_a-pathfn.obj: $(top_builddir)/../utils/pathfn.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT $(top_builddir)/../utils/libconfigparse_a-pathfn.obj -MD -MP -MF $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Tpo -c -o $(top_builddir)/../utils/libconfigparse_a-pathfn.obj `if test -f '$(top_builddir)/../utils/pathfn.c'; then $(CYGPATH_W) '$(top_builddir)/../utils/pathfn.c'; else $(CYGPATH_W) '$(srcdir)/$(top_builddir)/../utils/pathfn.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Tpo $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/../utils/pathfn.c' object='$(top_builddir)/../utils/libconfigparse_a-pathfn.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o $(top_builddir)/../utils/libconfigparse_a-pathfn.obj `if test -f '$(top_builddir)/../utils/pathfn.c'; then $(CYGPATH_W) '$(top_builddir)/../utils/pathfn.c'; else $(CYGPATH_W) '$(srcdir)/$(top_builddir)/../utils/pathfn.c'; fi` + +$(top_builddir)/../utils/libconfigparse_a-keyfile.o: $(top_builddir)/../utils/keyfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT $(top_builddir)/../utils/libconfigparse_a-keyfile.o -MD -MP -MF $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Tpo -c -o $(top_builddir)/../utils/libconfigparse_a-keyfile.o `test -f '$(top_builddir)/../utils/keyfile.c' || echo '$(srcdir)/'`$(top_builddir)/../utils/keyfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Tpo $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/../utils/keyfile.c' object='$(top_builddir)/../utils/libconfigparse_a-keyfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o $(top_builddir)/../utils/libconfigparse_a-keyfile.o `test -f '$(top_builddir)/../utils/keyfile.c' || echo '$(srcdir)/'`$(top_builddir)/../utils/keyfile.c + +$(top_builddir)/../utils/libconfigparse_a-keyfile.obj: $(top_builddir)/../utils/keyfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT $(top_builddir)/../utils/libconfigparse_a-keyfile.obj -MD -MP -MF $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Tpo -c -o $(top_builddir)/../utils/libconfigparse_a-keyfile.obj `if test -f '$(top_builddir)/../utils/keyfile.c'; then $(CYGPATH_W) '$(top_builddir)/../utils/keyfile.c'; else $(CYGPATH_W) '$(srcdir)/$(top_builddir)/../utils/keyfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Tpo $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/../utils/keyfile.c' object='$(top_builddir)/../utils/libconfigparse_a-keyfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o $(top_builddir)/../utils/libconfigparse_a-keyfile.obj `if test -f '$(top_builddir)/../utils/keyfile.c'; then $(CYGPATH_W) '$(top_builddir)/../utils/keyfile.c'; else $(CYGPATH_W) '$(srcdir)/$(top_builddir)/../utils/keyfile.c'; fi` + +$(top_builddir)/../utils/libconfigparse_a-parseloc.o: $(top_builddir)/../utils/parseloc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT $(top_builddir)/../utils/libconfigparse_a-parseloc.o -MD -MP -MF $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Tpo -c -o $(top_builddir)/../utils/libconfigparse_a-parseloc.o `test -f '$(top_builddir)/../utils/parseloc.c' || echo '$(srcdir)/'`$(top_builddir)/../utils/parseloc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Tpo $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/../utils/parseloc.c' object='$(top_builddir)/../utils/libconfigparse_a-parseloc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o $(top_builddir)/../utils/libconfigparse_a-parseloc.o `test -f '$(top_builddir)/../utils/parseloc.c' || echo '$(srcdir)/'`$(top_builddir)/../utils/parseloc.c + +$(top_builddir)/../utils/libconfigparse_a-parseloc.obj: $(top_builddir)/../utils/parseloc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT $(top_builddir)/../utils/libconfigparse_a-parseloc.obj -MD -MP -MF $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Tpo -c -o $(top_builddir)/../utils/libconfigparse_a-parseloc.obj `if test -f '$(top_builddir)/../utils/parseloc.c'; then $(CYGPATH_W) '$(top_builddir)/../utils/parseloc.c'; else $(CYGPATH_W) '$(srcdir)/$(top_builddir)/../utils/parseloc.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Tpo $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/../utils/parseloc.c' object='$(top_builddir)/../utils/libconfigparse_a-parseloc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o $(top_builddir)/../utils/libconfigparse_a-parseloc.obj `if test -f '$(top_builddir)/../utils/parseloc.c'; then $(CYGPATH_W) '$(top_builddir)/../utils/parseloc.c'; else $(CYGPATH_W) '$(srcdir)/$(top_builddir)/../utils/parseloc.c'; fi` + $(top_builddir)/../utils/libspindlefe_la-pathfn.lo: $(top_builddir)/../utils/pathfn.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libspindlefe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT $(top_builddir)/../utils/libspindlefe_la-pathfn.lo -MD -MP -MF $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-pathfn.Tpo -c -o $(top_builddir)/../utils/libspindlefe_la-pathfn.lo `test -f '$(top_builddir)/../utils/pathfn.c' || echo '$(srcdir)/'`$(top_builddir)/../utils/pathfn.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-pathfn.Tpo $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-pathfn.Plo @@ -907,6 +1003,48 @@ $(top_builddir)/../utils/spindle-rshlaunch.obj: $(top_builddir)/../utils/rshlaun @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< +libconfigparse_a-config_mgr.o: config_mgr.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libconfigparse_a-config_mgr.o -MD -MP -MF $(DEPDIR)/libconfigparse_a-config_mgr.Tpo -c -o libconfigparse_a-config_mgr.o `test -f 'config_mgr.cc' || echo '$(srcdir)/'`config_mgr.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libconfigparse_a-config_mgr.Tpo $(DEPDIR)/libconfigparse_a-config_mgr.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_mgr.cc' object='libconfigparse_a-config_mgr.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libconfigparse_a-config_mgr.o `test -f 'config_mgr.cc' || echo '$(srcdir)/'`config_mgr.cc + +libconfigparse_a-config_mgr.obj: config_mgr.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libconfigparse_a-config_mgr.obj -MD -MP -MF $(DEPDIR)/libconfigparse_a-config_mgr.Tpo -c -o libconfigparse_a-config_mgr.obj `if test -f 'config_mgr.cc'; then $(CYGPATH_W) 'config_mgr.cc'; else $(CYGPATH_W) '$(srcdir)/config_mgr.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libconfigparse_a-config_mgr.Tpo $(DEPDIR)/libconfigparse_a-config_mgr.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_mgr.cc' object='libconfigparse_a-config_mgr.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libconfigparse_a-config_mgr.obj `if test -f 'config_mgr.cc'; then $(CYGPATH_W) 'config_mgr.cc'; else $(CYGPATH_W) '$(srcdir)/config_mgr.cc'; fi` + +libconfigparse_a-config_parser.o: config_parser.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libconfigparse_a-config_parser.o -MD -MP -MF $(DEPDIR)/libconfigparse_a-config_parser.Tpo -c -o libconfigparse_a-config_parser.o `test -f 'config_parser.cc' || echo '$(srcdir)/'`config_parser.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libconfigparse_a-config_parser.Tpo $(DEPDIR)/libconfigparse_a-config_parser.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_parser.cc' object='libconfigparse_a-config_parser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libconfigparse_a-config_parser.o `test -f 'config_parser.cc' || echo '$(srcdir)/'`config_parser.cc + +libconfigparse_a-config_parser.obj: config_parser.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libconfigparse_a-config_parser.obj -MD -MP -MF $(DEPDIR)/libconfigparse_a-config_parser.Tpo -c -o libconfigparse_a-config_parser.obj `if test -f 'config_parser.cc'; then $(CYGPATH_W) 'config_parser.cc'; else $(CYGPATH_W) '$(srcdir)/config_parser.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libconfigparse_a-config_parser.Tpo $(DEPDIR)/libconfigparse_a-config_parser.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_parser.cc' object='libconfigparse_a-config_parser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libconfigparse_a-config_parser.obj `if test -f 'config_parser.cc'; then $(CYGPATH_W) 'config_parser.cc'; else $(CYGPATH_W) '$(srcdir)/config_parser.cc'; fi` + +libconfigparse_a-parseargs.o: parseargs.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libconfigparse_a-parseargs.o -MD -MP -MF $(DEPDIR)/libconfigparse_a-parseargs.Tpo -c -o libconfigparse_a-parseargs.o `test -f 'parseargs.cc' || echo '$(srcdir)/'`parseargs.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libconfigparse_a-parseargs.Tpo $(DEPDIR)/libconfigparse_a-parseargs.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parseargs.cc' object='libconfigparse_a-parseargs.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libconfigparse_a-parseargs.o `test -f 'parseargs.cc' || echo '$(srcdir)/'`parseargs.cc + +libconfigparse_a-parseargs.obj: parseargs.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libconfigparse_a-parseargs.obj -MD -MP -MF $(DEPDIR)/libconfigparse_a-parseargs.Tpo -c -o libconfigparse_a-parseargs.obj `if test -f 'parseargs.cc'; then $(CYGPATH_W) 'parseargs.cc'; else $(CYGPATH_W) '$(srcdir)/parseargs.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libconfigparse_a-parseargs.Tpo $(DEPDIR)/libconfigparse_a-parseargs.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parseargs.cc' object='libconfigparse_a-parseargs.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigparse_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libconfigparse_a-parseargs.obj `if test -f 'parseargs.cc'; then $(CYGPATH_W) 'parseargs.cc'; else $(CYGPATH_W) '$(srcdir)/parseargs.cc'; fi` + libspindlefe_la-spindle_fe.lo: spindle_fe.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libspindlefe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libspindlefe_la-spindle_fe.lo -MD -MP -MF $(DEPDIR)/libspindlefe_la-spindle_fe.Tpo -c -o libspindlefe_la-spindle_fe.lo `test -f 'spindle_fe.cc' || echo '$(srcdir)/'`spindle_fe.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libspindlefe_la-spindle_fe.Tpo $(DEPDIR)/libspindlefe_la-spindle_fe.Plo @@ -1288,7 +1426,7 @@ distdir-am: $(DISTFILES) done check-am: all-am check: check-am -all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) +all-am: Makefile $(PROGRAMS) $(LIBRARIES) $(LTLIBRARIES) $(HEADERS) install-binPROGRAMS: install-libLTLIBRARIES installdirs: @@ -1330,10 +1468,14 @@ maintainer-clean-generic: clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ - clean-libtool clean-noinstLTLIBRARIES mostlyclean-am + clean-libtool clean-noinstLIBRARIES clean-noinstLTLIBRARIES \ + mostlyclean-am distclean: distclean-am - -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-keyfile.Plo + -rm -f $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Po + -rm -f $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Po + -rm -f $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Po + -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-keyfile.Plo -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-parseloc.Plo -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-pathfn.Plo -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-rshlaunch.Plo @@ -1345,6 +1487,9 @@ distclean: distclean-am -rm -f $(top_builddir)/../utils/$(DEPDIR)/spindle-parseloc.Po -rm -f $(top_builddir)/../utils/$(DEPDIR)/spindle-pathfn.Po -rm -f $(top_builddir)/../utils/$(DEPDIR)/spindle-rshlaunch.Po + -rm -f ./$(DEPDIR)/libconfigparse_a-config_mgr.Po + -rm -f ./$(DEPDIR)/libconfigparse_a-config_parser.Po + -rm -f ./$(DEPDIR)/libconfigparse_a-parseargs.Po -rm -f ./$(DEPDIR)/libspindlefe_la-config_mgr.Plo -rm -f ./$(DEPDIR)/libspindlefe_la-config_parser.Plo -rm -f ./$(DEPDIR)/libspindlefe_la-parse_preload.Plo @@ -1414,7 +1559,10 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-keyfile.Plo + -rm -f $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-keyfile.Po + -rm -f $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-parseloc.Po + -rm -f $(top_builddir)/../utils/$(DEPDIR)/libconfigparse_a-pathfn.Po + -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-keyfile.Plo -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-parseloc.Plo -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-pathfn.Plo -rm -f $(top_builddir)/../utils/$(DEPDIR)/libspindlefe_la-rshlaunch.Plo @@ -1426,6 +1574,9 @@ maintainer-clean: maintainer-clean-am -rm -f $(top_builddir)/../utils/$(DEPDIR)/spindle-parseloc.Po -rm -f $(top_builddir)/../utils/$(DEPDIR)/spindle-pathfn.Po -rm -f $(top_builddir)/../utils/$(DEPDIR)/spindle-rshlaunch.Po + -rm -f ./$(DEPDIR)/libconfigparse_a-config_mgr.Po + -rm -f ./$(DEPDIR)/libconfigparse_a-config_parser.Po + -rm -f ./$(DEPDIR)/libconfigparse_a-parseargs.Po -rm -f ./$(DEPDIR)/libspindlefe_la-config_mgr.Plo -rm -f ./$(DEPDIR)/libspindlefe_la-config_parser.Plo -rm -f ./$(DEPDIR)/libspindlefe_la-parse_preload.Plo @@ -1473,16 +1624,16 @@ uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ - clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ - ctags-am distclean distclean-compile distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-binPROGRAMS \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-includeHEADERS install-info install-info-am \ - install-libLTLIBRARIES install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ + clean-libtool clean-noinstLIBRARIES clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-includeHEADERS install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am 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 tags-am uninstall uninstall-am uninstall-binPROGRAMS \ diff --git a/src/utils/keyfile.c b/src/utils/keyfile.c index 9a9d8c8f..e1d8658f 100644 --- a/src/utils/keyfile.c +++ b/src/utils/keyfile.c @@ -142,3 +142,12 @@ void clean_keyfile(uint64_t unique_id) unlink(path); debug_printf("Cleaned keyfile %s\n", path); } + +const char* get_sec_keydir_path(void) +{ +#if defined(KEYFILE) && defined(SEC_KEYDIR) + return SEC_KEYDIR; +#else + return NULL; +#endif +} diff --git a/src/utils/keyfile.h b/src/utils/keyfile.h index 787eabd9..bfb92d06 100644 --- a/src/utils/keyfile.h +++ b/src/utils/keyfile.h @@ -27,6 +27,7 @@ void create_key(unsigned char *buffer, int key_size_bytes); void get_keyfile_path(char *pathname, int pathname_len, uint64_t unique_id); void create_keyfile(uint64_t unique_id); void clean_keyfile(uint64_t unique_id); +const char* get_sec_keydir_path(void); #if defined(__cplusplus) } diff --git a/testsuite/config_helper/README.md b/testsuite/config_helper/README.md new file mode 100644 index 00000000..77e428d9 --- /dev/null +++ b/testsuite/config_helper/README.md @@ -0,0 +1,65 @@ +# spindle-config-helper Test Suite + +Simple integration tests for the `spindle-config-helper` tool. + +## Running Tests + +```bash +# From the build directory +cd testsuite/config_helper +./run_tests.sh /path/to/spindle-config-helper + +# Or let it find the default location +./run_tests.sh +``` + +## Test Coverage + +1. **test_01_smoke** - Basic execution (does it run without crashing?) +2. **test_02_help** - Help flag works +3. **test_03_version** - Version flag works +4. **test_04_local_fs** - Detects local filesystems correctly (/dev/shm, /tmp) +5. **test_05_undefined_var** - Warns about undefined variables +6. **test_06_empty_var** - Detects empty variables creating // in paths +7. **test_07_multiple_paths** - Handles colon-separated path lists +8. **test_08_kernel_fs** - Recognizes kernel filesystems (/proc, /sys, /dev) +9. **test_09_permissions** - Checks parent directory permissions +10. **test_10_relative_path** - Resolves relative paths correctly +11. **test_11_pythonprefix_valid** - Validates Python installation (skips if /usr/tce missing) +12. **test_12_pythonprefix_invalid** - Warns when path lacks bin/python +13. **test_13_pythonprefix_undefined_var** - Detects undefined variables in pythonprefix +14. **test_14_security_mode_detection** - Detects and reports security configuration +15. **test_15_security_none_warning** - Warns when security=none (insecure) +16. **test_16_munge_detection** - Reports munge availability +17. **test_17_launchmon_warning** - Warns when security=launchmon (deprecated) + +## Sample Mount Files + +The `sample_mounts/` directory contains example `/proc/mounts` files for testing: + +- **simple.mounts** - Basic system with NFS and local storage +- **hpc.mounts** - HPC system with Lustre, GPFS, and local storage +- **container.mounts** - Container environment with overlay filesystem + +These can be used for future mocking/testing if we implement a `--test-mounts` flag. + +## Exit Codes + +- `0` - All tests passed +- `1` - One or more tests failed + +## Adding New Tests + +1. Add a `test_XX_description()` function to `run_tests.sh` +2. Add `run_test "XX_description" test_XX_description` to the test list +3. Test should return 0 on success, non-zero on failure +4. Use `grep -q` for output validation to keep tests simple + +## Design Philosophy + +These are **integration tests**, not unit tests: +- Run the actual tool binary +- Use real filesystem operations where possible +- Keep tests simple and maintainable +- Focus on catching regressions, not exhaustive coverage +- Avoid heavy mocking - trust the implementation for complex logic diff --git a/testsuite/config_helper/run_tests.sh b/testsuite/config_helper/run_tests.sh new file mode 100755 index 00000000..29358f02 --- /dev/null +++ b/testsuite/config_helper/run_tests.sh @@ -0,0 +1,188 @@ +#!/bin/bash +# Test suite for spindle-config-helper +# Usage: ./run_tests.sh [path-to-spindle-config-helper] + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +HELPER="${1:-../../build/config_helper-serial/src/config_helper/spindle-config-helper}" + +if [[ ! -x "$HELPER" ]]; then + echo "Error: spindle-config-helper not found or not executable: $HELPER" + echo "Usage: $0 [path-to-spindle-config-helper]" + exit 1 +fi + +TESTS_PASSED=0 +TESTS_FAILED=0 +FAILED_TESTS="" + +# Test helper function +run_test() { + local test_name="$1" + local test_func="$2" + + echo -n "Running test: $test_name ... " + + if $test_func 2>&1 >/dev/null; then + echo "PASSED" + ((TESTS_PASSED++)) + return 0 + else + echo "FAILED" + ((TESTS_FAILED++)) + FAILED_TESTS="$FAILED_TESTS\n - $test_name" + return 1 + fi +} + +# Test 1: Basic smoke test - does it run? +test_01_smoke() { + $HELPER --commpaths=/tmp --cachepaths=/tmp >/dev/null 2>&1 +} + +# Test 2: Help flag works +test_02_help() { + $HELPER --help 2>&1 | grep -q "Usage:" +} + +# Test 3: Version flag works +test_03_version() { + $HELPER --version 2>&1 | grep -q "spindle-config-helper version" +} + +# Test 4: Local filesystem detection +test_04_local_fs() { + output=$($HELPER --commpaths=/dev/shm --cachepaths=/tmp 2>&1) + echo "$output" | grep -q "OK: /dev/shm" && \ + echo "$output" | grep -q "OK: /tmp" +} + +# Test 5: Undefined variable detection +test_05_undefined_var() { + unset SPINDLE_TEST_VAR + output=$($HELPER --commpaths='$SPINDLE_TEST_VAR' 2>&1) + echo "$output" | grep -q "Variable is not set" +} + +# Test 6: Empty variable detection +test_06_empty_var() { + export SPINDLE_TEST_VAR="" + output=$($HELPER --commpaths='/tmp/$SPINDLE_TEST_VAR/dir' 2>&1) + echo "$output" | grep -q "empty segment" +} + +# Test 7: Multiple paths with colon separator +test_07_multiple_paths() { + output=$($HELPER --commpaths=/tmp:/dev/shm 2>&1) + echo "$output" | grep -q "OK: /tmp" && \ + echo "$output" | grep -q "OK: /dev/shm" +} + +# Test 8: Kernel filesystem handling +test_08_kernel_fs() { + output=$($HELPER --local-prefix=/proc:/sys:/dev 2>&1) + echo "$output" | grep -q "OK: /proc" && \ + echo "$output" | grep -q "OK: /sys" && \ + echo "$output" | grep -q "OK: /dev" +} + +# Test 9: Permission check for non-existent paths +test_09_permissions() { + # /tmp should be writable - warns about non-existence but shows it can be created + output=$($HELPER --commpaths=/tmp/spindle_test_nonexistent_dir 2>&1) + echo "$output" | grep -q "/tmp/spindle_test_nonexistent_dir" && \ + echo "$output" | grep -q "will be created by Spindle" +} + +# Test 10: Relative path resolution +test_10_relative_path() { + cd /tmp + output=$($HELPER --commpaths=./relative_test 2>&1) + echo "$output" | grep -q "/tmp/relative_test" + cd "$SCRIPT_DIR" +} + +# Test 11: pythonprefix with valid installation +test_11_pythonprefix_valid() { + # Use /usr/tce if it exists, otherwise skip + if [[ ! -x /usr/tce/bin/python3 ]]; then + return 0 # Skip on systems without /usr/tce + fi + output=$($HELPER --python-prefix=/usr/tce 2>&1) + echo "$output" | grep -q "OK: /usr/tce" +} + +# Test 12: pythonprefix without Python binaries +test_12_pythonprefix_invalid() { + output=$($HELPER --python-prefix=/tmp 2>&1) + echo "$output" | grep -q "No bin/python or bin/python3 found" +} + +# Test 13: pythonprefix with undefined variable +test_13_pythonprefix_undefined_var() { + unset PYTHON_HOME + output=$($HELPER --python-prefix='$PYTHON_HOME' 2>&1) + echo "$output" | grep -q "Variable is not set" +} + +# Test 14: Security mode detection +test_14_security_mode_detection() { + output=$($HELPER --commpaths=/tmp 2>&1) + echo "$output" | grep -q "# Security configuration:" && \ + echo "$output" | grep -q "Current mode:" +} + +# Test 15: Security none warning +test_15_security_none_warning() { + output=$($HELPER --commpaths=/tmp --security=none 2>&1) + echo "$output" | grep -q "WARNING: No authentication enabled" +} + +# Test 16: Munge detection +test_16_munge_detection() { + output=$($HELPER --commpaths=/tmp 2>&1) + # Check that munge availability is reported + echo "$output" | grep -q "Munge availability:" +} + +# Test 17: LaunchMON warning +test_17_launchmon_warning() { + output=$($HELPER --commpaths=/tmp --security=launchmon 2>&1) + echo "$output" | grep -q "WARNING: LaunchMON security mode" +} + +# Run all tests +echo "=========================================" +echo "spindle-config-helper Test Suite" +echo "=========================================" +echo "Testing: $HELPER" +echo "" + +run_test "01_smoke_test" test_01_smoke +run_test "02_help_flag" test_02_help +run_test "03_version_flag" test_03_version +run_test "04_local_filesystem_detection" test_04_local_fs +run_test "05_undefined_variable" test_05_undefined_var +run_test "06_empty_variable" test_06_empty_var +run_test "07_multiple_paths" test_07_multiple_paths +run_test "08_kernel_filesystems" test_08_kernel_fs +run_test "09_permission_check" test_09_permissions +run_test "10_relative_path_resolution" test_10_relative_path +run_test "11_pythonprefix_valid" test_11_pythonprefix_valid +run_test "12_pythonprefix_invalid" test_12_pythonprefix_invalid +run_test "13_pythonprefix_undefined_var" test_13_pythonprefix_undefined_var +run_test "14_security_mode_detection" test_14_security_mode_detection +run_test "15_security_none_warning" test_15_security_none_warning +run_test "16_munge_detection" test_16_munge_detection +run_test "17_launchmon_warning" test_17_launchmon_warning + +echo "" +echo "=========================================" +echo "Results: $TESTS_PASSED passed, $TESTS_FAILED failed" +echo "=========================================" + +if [[ $TESTS_FAILED -gt 0 ]]; then + echo -e "Failed tests:$FAILED_TESTS" + exit 1 +fi + +exit 0 diff --git a/testsuite/config_helper/sample_mounts/container.mounts b/testsuite/config_helper/sample_mounts/container.mounts new file mode 100644 index 00000000..57e25043 --- /dev/null +++ b/testsuite/config_helper/sample_mounts/container.mounts @@ -0,0 +1,6 @@ +overlay / overlay rw,relatime,lowerdir=/sysroot,upperdir=/overlay/rw,workdir=/overlay/work 0 0 +tmpfs /dev/shm tmpfs rw,nosuid,nodev,size=8G 0 0 +tmpfs /tmp tmpfs rw,nosuid,nodev,size=16G 0 0 +proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 +sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 +devtmpfs /dev devtmpfs rw,relatime,size=4G 0 0 diff --git a/testsuite/config_helper/sample_mounts/hpc.mounts b/testsuite/config_helper/sample_mounts/hpc.mounts new file mode 100644 index 00000000..37f08a3d --- /dev/null +++ b/testsuite/config_helper/sample_mounts/hpc.mounts @@ -0,0 +1,8 @@ +tmpfs /dev/shm tmpfs rw,nosuid,nodev,size=128G 0 0 +tmpfs /tmp tmpfs rw,nosuid,nodev,size=100G 0 0 +lustre-mdt:/scratch /scratch lustre rw,lazystatfs 0 0 +gpfs-server:/gpfs/project /gpfs/project gpfs rw,relatime 0 0 +nfs-server:/home /home nfs4 rw,relatime,vers=4.2 0 0 +/dev/nvme0n1 /local ext4 rw,relatime 0 0 +proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 +sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 diff --git a/testsuite/config_helper/sample_mounts/simple.mounts b/testsuite/config_helper/sample_mounts/simple.mounts new file mode 100644 index 00000000..0d23a0c4 --- /dev/null +++ b/testsuite/config_helper/sample_mounts/simple.mounts @@ -0,0 +1,7 @@ +tmpfs /dev/shm tmpfs rw,nosuid,nodev,size=64G 0 0 +tmpfs /tmp tmpfs rw,nosuid,nodev,size=32G 0 0 +nfs-server:/exports/home /home nfs4 rw,relatime,vers=4.1 0 0 +/dev/sda1 /data ext4 rw,relatime 0 0 +proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 +sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 +devtmpfs /dev devtmpfs rw,nosuid,size=16G,mode=755 0 0 diff --git a/testsuite/run_crash_tests_template.sh b/testsuite/run_crash_tests_template.sh index 1e8e2bf9..03b04379 100755 --- a/testsuite/run_crash_tests_template.sh +++ b/testsuite/run_crash_tests_template.sh @@ -35,6 +35,7 @@ die() { echo "FAIL: $*" >&2; exit 1; } # flags (comma-separated): "multi-rank" skips the mode on a single rank; # "clean" expects the test to NOT crash; "altstack" runs the mode with # --crash-altstack; "nofollowfork" runs it with --follow-fork=false; +# "nocrashhandler" runs it without the crash handler; # "forkchild" means rows may come from fork or exec children of a rank. # top_frame_regex: regex that should match the top frame in produced coredumps. # Note that all threads will be checked, so in multithreaded examples the regex should @@ -70,6 +71,7 @@ CRASH_TESTS=( 'safepoint-fix-write ; 0 ; 0 ; clean ; -' 'safepoint-fix-write-altstack ; 0 ; 0 ; clean,altstack ; - ; ; ; safepoint-fix-write' 'safepoint-longjmp ; 0 ; 0 ; clean ; -' + 'safepoint-nocrashhandler ; 0 ; 0 ; clean,nocrashhandler ; - ; ; ; safepoint' 'safepoint-span-read ; 0 ; 0 ; clean ; -' 'safepoint-span-write ; 0 ; 0 ; clean ; -' 'safepoint-span-bad-read ; 1 ; N ; ; do_safepoint_span_bad_read' @@ -266,6 +268,11 @@ launch() { cmdline_opts="$cmdline_opts --follow-fork=false" flux_opts+=(-o spindle.follow-fork=no) fi + if has_flag "$mode" nocrashhandler; then + cmdline_opts="" + flux_opts=(-o spindle) + export SPINDLE_NO_CRASH_DEDUP=1 + fi ulimit -c unlimited # Make libcrashfuncs.so visible to dlopen() from the mode's scratch dir. export LD_LIBRARY_PATH="$TESTDIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" @@ -290,7 +297,7 @@ launch() { ;; slurm-plugin) salloc -N"$NODES" -n"$NODES" \ - srun --spindle="$cmdline_opts" \ + srun "--spindle${cmdline_opts:+=$cmdline_opts}" \ "$binary" --crash-mode "$crash_mode" ;; esac