diff --git a/CMakeLists.txt b/CMakeLists.txt index 677bbed2b..36de27049 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -437,15 +437,15 @@ endif() # enable "make test" enable_testing() +# configuration files +add_subdirectory(config) + # build the core executables add_subdirectory(src) # build the core scripts add_subdirectory(scripts) -# add configuration examples -add_subdirectory(config) - # build documentation add_subdirectory(docs) diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index 13e4bff65..a52fa7647 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -65,22 +65,43 @@ cmake_minimum_required(VERSION 3.1.0) include(ProcessorCount) ProcessorCount(CORES) +set(CONFIG_FILE "/etc/GUFI/config" CACHE FILEPATH "Where high level GUFI tools will look for configuration") +if (NOT IS_ABSOLUTE "${CONFIG_FILE}") + message(FATAL_ERROR "CONFIG_FILE must be set to absolute path") +endif() +if (EXISTS "${CONFIG_FILE}") + execute_process(COMMAND stat --dereference --format "%F" "${CONFIG_FILE}" + OUTPUT_VARIABLE CONFIG_FILE_TYPE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (NOT CONFIG_FILE_TYPE STREQUAL "regular file") + message(FATAL_ERROR "CONFIG_FILE must point to a file. Got ${CONFIG_FILE_TYPE}") + endif() +endif() +if (NOT CONFIG_FILE STREQUAL "/etc/GUFI/config") + message(WARNING "CONFIG_FILE ${CONFIG_FILE} may conflict with filesystem package when installing GUFI RPMs") +endif() + +get_filename_component(CONFIG_FILE_PARENT "${CONFIG_FILE}" DIRECTORY) + if (CLIENT) # client config # copy the hard coded client example file - configure_file(client.example.in ${CMAKE_BINARY_DIR}/client.example @ONLY) + configure_file(client.example.in "${CMAKE_BINARY_DIR}/client.example" @ONLY) # make install and rpm - install(FILES ${CMAKE_BINARY_DIR}/client.example - DESTINATION /etc/GUFI - COMPONENT Client) + install(FILES "${CMAKE_BINARY_DIR}/client.example" + DESTINATION "${CONFIG_FILE_PARENT}" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE WORLD_READ # 664 + COMPONENT Client) endif() # server config # copy the hard coded server example file -configure_file(server.example.in ${CMAKE_BINARY_DIR}/server.example @ONLY) +configure_file(server.example.in "${CMAKE_BINARY_DIR}/server.example" @ONLY) # make install and rpm -install(FILES ${CMAKE_BINARY_DIR}/server.example - DESTINATION /etc/GUFI +install(FILES "${CMAKE_BINARY_DIR}/server.example" + DESTINATION "${CONFIG_FILE_PARENT}" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE WORLD_READ # 664 COMPONENT Server) diff --git a/contrib/CI/generic_rpm.sh.in b/contrib/CI/generic_rpm.sh.in index 0dcd53198..d4a32bdc0 100755 --- a/contrib/CI/generic_rpm.sh.in +++ b/contrib/CI/generic_rpm.sh.in @@ -76,7 +76,7 @@ make package yum install -y gufi-*.rpm # compare the configuration files -@DIFF@ server.example /etc/GUFI/server.example +@DIFF@ server.example "$(dirname @CONFIG_FILE@)/server.example" # remove the configuration file -rm -r /etc/GUFI +rm -r "$(dirname @CONFIG_FILE@)" diff --git a/contrib/CI/rpms.sh.in b/contrib/CI/rpms.sh.in index 99d4de10c..7a7f5651a 100755 --- a/contrib/CI/rpms.sh.in +++ b/contrib/CI/rpms.sh.in @@ -76,7 +76,7 @@ make package yum install -y gufi-server-*.rpm # compare the configuration files -@DIFF@ server.example /etc/GUFI/server.example +@DIFF@ server.example "$(dirname @CONFIG_FILE@)/server.example" # uninstall GUFI-Server yum autoremove -y GUFI-Server @@ -88,4 +88,4 @@ yum autoremove -y GUFI-Server yum install -y gufi-client-*.rpm # compare the configuration files -@DIFF@ client.example /etc/GUFI/client.example +@DIFF@ client.example "$(dirname @CONFIG_FILE@)/client.example" diff --git a/docs/latex/sections/build.tex b/docs/latex/sections/build.tex index e0d7ab250..91fb03912 100644 --- a/docs/latex/sections/build.tex +++ b/docs/latex/sections/build.tex @@ -170,6 +170,25 @@ \subsubsection{Debug} \end{tabularx} \end{table} +\subsubsection{System Paths} +Some files are installed into system paths that are not +\texttt{\$\{CMAKE\_INSTALL\_PREFIX\}/bin} or \texttt{\$\{CMAKE\_INSTALL\_PREFIX\}/lib}. + +\begin{table}[H] +\centering +\begin{tabularx}{1.2\textwidth}{| l | X |} + \hline + \texttt{-D=} & Description \\ + \hline + \texttt{CONFIG\_FILE=} & Path of configuration file used by scripts. \\ + & Defaults to \guficonfigfile. \\ + & Note that many paths will conflict with the paths of other \\ + & packages if installing GUFI via package. If installing GUFI \\ + & with \texttt{make install}, point to a convenient location. \\ + \hline +\end{tabularx} +\end{table} + \subsubsection{Client} \begin{table}[H] \centering diff --git a/docs/latex/sections/deploy.tex b/docs/latex/sections/deploy.tex index 748139b12..7043256dc 100644 --- a/docs/latex/sections/deploy.tex +++ b/docs/latex/sections/deploy.tex @@ -92,16 +92,21 @@ \subsection{Runtime Configuration} server has the actual implementation of the wrappers while the clients have command forwarders. -These wrappers require a valid GUFI configuration file to function correctly. -By default, they assume this file is available (with read permissions for all -GUFI users) at \guficonfigfile; alternatively, different paths can be specified -via a \texttt{GUFI\_CONFIG} environment variable. These configuration files are -simple text files containing lines of \texttt{=}. Duplicate -configuration keys are overwritten by the bottom-most line. Empty lines and -lines starting with \# are ignored. The server and client require different -configuration values. Example configuration files can be found in the +These wrappers require a valid GUFI configuration file to function +correctly. The path to the configuration file can be set at configure +time with the \texttt{CONFIG\_FILE} CMake variable (defaults to +\guficonfigfile). The configuration file will be readable by all users +but should only be writable by the administrators (664 permissions) in +order to prevent non-admin users from changing how GUFI runs. + +Configuration files are simple text files containing lines of +\texttt{=}. Duplicate configuration keys are overwritten +by the bottom-most line. Empty lines and lines starting with \# are +ignored. The server and client require different configuration +values. Example configuration files can be found in the \texttt{config} directory of the GUFI source, as well as in the build -directory. The corresponding examples will also be installed with GUFI. +directory. The corresponding examples will also be installed with +GUFI. If a server and client exist on the same node, the server and client configuration file may be combined into one file. diff --git a/docs/latex/sections/user_env.tex b/docs/latex/sections/user_env.tex index 80c7d574c..987f9ce38 100644 --- a/docs/latex/sections/user_env.tex +++ b/docs/latex/sections/user_env.tex @@ -62,13 +62,13 @@ \section{Environment} -Users interact with GUFI using \gufifind, \gufils, -\gufistat, and \gufistats. These are wrapper scripts -that access the actual implementations of these files. +Users interact with GUFI using \gufifind, \gufils, \gufistat, and +\gufistats. These are wrapper scripts that access the actual +implementations of these files. -There should be a file readable (but not necessarily modifiable) by -all GUFI users called \guficonfigfile. This file specifies where the -GUFI server is, as the actual GUFI trees are not expected to be -locally available. For experimental GUFI installations such as testing -and developing, alternative paths to the GUFI configuration file can -be specified via a \texttt{GUFI\_CONFIG} environment variable. +These wrapper scripts use a configuration file (that is readable but +not necessarily modifiable by users) that specifies where the GUFI +server is, as the actual GUFI trees are not expected to be locally +available. For experimental GUFI installations, alternative paths to +the GUFI configuration file can be specified via the +\texttt{CONFIG\_FILE} CMake variable. diff --git a/docs/man/gufi_find.1.in b/docs/man/gufi_find.1.in index 55be9f952..cde2d3871 100644 --- a/docs/man/gufi_find.1.in +++ b/docs/man/gufi_find.1.in @@ -107,7 +107,7 @@ Size of each thread's output buffer .Sh FILES .Bl -tag -width -compact .It Pa @CMAKE_INSTALL_PREFIX@/@BIN@/gufi_find -.It Pa /etc/GUFI/config +.It Pa @CONFIG_FILE@ .El .\" .Sh BUGS diff --git a/docs/man/gufi_ls.1.in b/docs/man/gufi_ls.1.in index fcc0ed5f8..ccf443be0 100644 --- a/docs/man/gufi_ls.1.in +++ b/docs/man/gufi_ls.1.in @@ -69,7 +69,7 @@ Size of each thread's output buffer .Sh FILES .Bl -tag -width -compact .It Pa @CMAKE_INSTALL_PREFIX@/@BIN@/gufi_ls -.It Pa /etc/GUFI/config +.It Pa @CONFIG_FILE@ .El .\" .Sh BUGS diff --git a/docs/man/gufi_stats.1.in b/docs/man/gufi_stats.1.in index d619a4152..eaa3b2752 100644 --- a/docs/man/gufi_stats.1.in +++ b/docs/man/gufi_stats.1.in @@ -79,7 +79,7 @@ Total number of leaf directories under the provided path .Sh FILES .Bl -tag -width -compact .It Pa @CMAKE_INSTALL_PREFIX@/@BIN@/gufi_stats -.It Pa /etc/GUFI/config +.It Pa @CONFIG_FILE@ .El .\" .Sh BUGS diff --git a/scripts/bash_completion b/scripts/bash_completion index 127604a50..761a03626 100644 --- a/scripts/bash_completion +++ b/scripts/bash_completion @@ -63,7 +63,7 @@ GUFI_PWD="" -CONFIG="${1-/etc/GUFI/config}" +CONFIG="${1-@CONFIG_FILE@}" __gufi() { # make sure the config file exists diff --git a/scripts/gufi_client.py b/scripts/gufi_client.py index 34c819d90..b9092a8a2 100755 --- a/scripts/gufi_client.py +++ b/scripts/gufi_client.py @@ -105,7 +105,7 @@ def talk_to_server(config, args): ssh.close() def run(args): - config = gufi_config.Client(gufi_config.DEFAULT_PATH) + config = gufi_config.Client(gufi_config.PATH) talk_to_server(config, args) if __name__ == '__main__': diff --git a/scripts/gufi_config.py b/scripts/gufi_config.py index 8d7a58ae6..3741619f8 100755 --- a/scripts/gufi_config.py +++ b/scripts/gufi_config.py @@ -68,17 +68,10 @@ import gufi_common # configuration file location -DEFAULT_PATH = '/etc/GUFI/config' - -# helper function to allow configurable paths to the gufi config file -def config_path(): - gufi_config_env = 'GUFI_CONFIG' - if gufi_config_env in os.environ: - return os.environ[gufi_config_env] - return DEFAULT_PATH +PATH = '@CONFIG_FILE@' class Config(object): # pylint: disable=too-few-public-methods,useless-object-inheritance - def __init__(self, settings, config_reference=DEFAULT_PATH): + def __init__(self, settings, config_reference=PATH): # path string if isinstance(config_reference, str): with open(config_reference, 'r') as config_file: # pylint: disable=unspecified-encoding diff --git a/scripts/gufi_find.py b/scripts/gufi_find.py index 33d76dad7..9da52624c 100755 --- a/scripts/gufi_find.py +++ b/scripts/gufi_find.py @@ -910,4 +910,4 @@ def run(argv, config_path): return query.returncode if __name__ == '__main__': - sys.exit(run(sys.argv, gufi_config.config_path())) + sys.exit(run(sys.argv, gufi_config.PATH)) diff --git a/scripts/gufi_getfattr.py b/scripts/gufi_getfattr.py index 1f75ee7b7..74885d5b3 100755 --- a/scripts/gufi_getfattr.py +++ b/scripts/gufi_getfattr.py @@ -242,4 +242,4 @@ def run(argv, config_path): return rc if __name__ == '__main__': - sys.exit(run(sys.argv, gufi_config.config_path())) + sys.exit(run(sys.argv, gufi_config.PATH)) diff --git a/scripts/gufi_ls.py b/scripts/gufi_ls.py index b0fa43461..9378506aa 100755 --- a/scripts/gufi_ls.py +++ b/scripts/gufi_ls.py @@ -408,4 +408,4 @@ def run(argv, config_path): return rc if __name__ == '__main__': - sys.exit(run(sys.argv, gufi_config.config_path())) + sys.exit(run(sys.argv, gufi_config.PATH)) diff --git a/scripts/gufi_stat.py b/scripts/gufi_stat.py index b31b75f84..6fda9f5b5 100755 --- a/scripts/gufi_stat.py +++ b/scripts/gufi_stat.py @@ -148,4 +148,4 @@ def run(argv, config_path): return stat.returncode if __name__ == '__main__': - sys.exit(run(sys.argv, gufi_config.config_path())) + sys.exit(run(sys.argv, gufi_config.PATH)) diff --git a/scripts/gufi_stats.py b/scripts/gufi_stats.py index f4812a058..6b3dc00bc 100755 --- a/scripts/gufi_stats.py +++ b/scripts/gufi_stats.py @@ -1195,4 +1195,4 @@ def run(argv, config_path): return query.returncode if __name__ == '__main__': - sys.exit(run(sys.argv, gufi_config.config_path())) + sys.exit(run(sys.argv, gufi_config.PATH)) diff --git a/test/unit/python/test_gufi_config.py b/test/unit/python/test_gufi_config.py index 459a7d10b..564addac2 100755 --- a/test/unit/python/test_gufi_config.py +++ b/test/unit/python/test_gufi_config.py @@ -95,11 +95,6 @@ class TestServerConfig(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name self.pairs = copy.deepcopy(TestServerConfig.default) - self.original_environ = os.environ.copy() # save this since we change it to test GUFI_CONFIG - - def tearDown(self): - os.environ.clear() - os.environ.update(self.original_environ) def check_values(self, config): self.assertEqual(TestServerConfig.default[gufi_config.Server.THREADS], @@ -141,16 +136,6 @@ def test_bad_threads(self): def test_bad_outputbuffer(self): self.bad_int(gufi_config.Server.OUTPUTBUFFER, ['-1', '', 'abc']) - # test the ability to change config path with an env var - def test_default_path(self): - if 'GUFI_CONFIG' in os.environ: - del os.environ['GUFI_CONFIG'] - self.assertEqual(gufi_config.config_path(), gufi_config.DEFAULT_PATH) - - def test_custom_path_from_env(self): - os.environ['GUFI_CONFIG'] = '/custom/path' - self.assertEqual(gufi_config.config_path(), '/custom/path') - class TestClientConfig(unittest.TestCase): default = { gufi_config.Client.SERVER : 'hostname',