From b4e034d9aa471c6244718914e08be3ee414b18e6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 5 Sep 2021 16:58:55 +0200 Subject: refactor gnunet-config to make preload hacks obsolete --- src/include/gnunet_configuration_lib.h | 158 ++++++++++++++ src/include/platform.h | 26 +++ src/util/Makefile.am | 1 + src/util/configuration_helper.c | 316 +++++++++++++++++++++++++++ src/util/gnunet-config.c | 386 ++------------------------------- 5 files changed, 518 insertions(+), 369 deletions(-) create mode 100644 src/util/configuration_helper.c (limited to 'src') diff --git a/src/include/gnunet_configuration_lib.h b/src/include/gnunet_configuration_lib.h index 02e656196..46b745541 100644 --- a/src/include/gnunet_configuration_lib.h +++ b/src/include/gnunet_configuration_lib.h @@ -632,6 +632,164 @@ GNUNET_CONFIGURATION_append_value_filename (struct const char *option, const char *value); + +/** + * Closure for #GNUNET_CONFIGURATION_config_tool_run() + * with settings for what should be done with the + * configuration. + */ +struct GNUNET_CONFIGURATION_ConfigSettings +{ + + /** + * Name of the section + */ + char *section; + + /** + * Name of the option + */ + char *option; + + /** + * Value to set + */ + char *value; + + /** + * Backend to check if the respective plugin is + * loadable. NULL if no check is to be performed. + * The value is the "basename" of the plugin to load. + */ + char *backend_check; + + /** + * Treat option as a filename. + */ + int is_filename; + + /** + * Whether to show the sections. + */ + int list_sections; + + /** + * Should we write out the configuration file, even if no value was changed? + */ + int rewrite; + + /** + * Should we give extra diagnostics? + */ + int diagnostics; + + /** + * Should the generated configuration file contain the whole configuration? + */ + int full; + + + /** + * Return value from the operation, to be returned + * from 'main'. + */ + int global_ret; + +}; + + +/** + * Macro that expands to a set of GNUNET-getopt directives + * to initialize a `struct GNUNET_CONFIGURATION_ConfigSettings` + * from the command line. + * + * @param cs configuration settings to initialize + */ +#define GNUNET_CONFIGURATION_CONFIG_OPTIONS(cs) \ + GNUNET_GETOPT_option_exclusive ( \ + GNUNET_GETOPT_option_string ( \ + 'b', \ + "supported-backend", \ + "BACKEND", \ + gettext_noop ( \ + "test if the current installation supports the specified BACKEND"), \ + &(cs)->backend_check)), \ + GNUNET_GETOPT_option_flag ( \ + 'F', \ + "full", \ + gettext_noop ( \ + "write the full configuration file, including default values"), \ + &(cs)->full), \ + GNUNET_GETOPT_option_flag ( \ + 'f', \ + "filename", \ + gettext_noop ("interpret option value as a filename (with $-expansion)"), \ + &(cs)->is_filename), \ + GNUNET_GETOPT_option_string ('o', \ + "option", \ + "OPTION", \ + gettext_noop ("name of the option to access"), \ + &(cs)->option), \ + GNUNET_GETOPT_option_flag ( \ + 'r', \ + "rewrite", \ + gettext_noop ( \ + "rewrite the configuration file, even if nothing changed"), \ + &(cs)->rewrite), \ + GNUNET_GETOPT_option_flag ( \ + 'd', \ + "diagnostics", \ + gettext_noop ( \ + "output extra diagnostics"), \ + &(cs)->diagnostics), \ + GNUNET_GETOPT_option_flag ('S', \ + "list-sections", \ + gettext_noop ( \ + "print available configuration sections"), \ + &(cs)->list_sections), \ + GNUNET_GETOPT_option_string ('s', \ + "section", \ + "SECTION", \ + gettext_noop ( \ + "name of the section to access"), \ + &(cs)->section), \ + GNUNET_GETOPT_option_string ('V', \ + "value", \ + "VALUE", \ + gettext_noop ("value to set"), \ + &(cs)->value) + + +/** + * Free resources assoicated with @a cs. + * + * @param[in] cs settings to free (actual memory + * of @a cs itself is not released) + */ +void +GNUNET_CONFIGURATION_config_settings_free ( + struct GNUNET_CONFIGURATION_ConfigSettings *cs); + + +/** + * Main task to run to perform operations typical for + * gnunet-config as per the configuration settings + * given in @a cls. + * + * @param cls closure with the `struct GNUNET_CONFIGURATION_ConfigSettings` + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, + * can be NULL!) + * @param cfg configuration + */ +void +GNUNET_CONFIGURATION_config_tool_run ( + void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg); + + #if 0 /* keep Emacsens' auto-indent happy */ { #endif diff --git a/src/include/platform.h b/src/include/platform.h index dfa30aeee..b1de45f62 100644 --- a/src/include/platform.h +++ b/src/include/platform.h @@ -252,6 +252,32 @@ atoll (const char *nptr); #define GNUNET_THREAD_LOCAL #endif + +/* LSB-style exit status codes */ +#ifndef EXIT_INVALIDARGUMENT +#define EXIT_INVALIDARGUMENT 2 +#endif + +#ifndef EXIT_NOTIMPLEMENTED +#define EXIT_NOTIMPLEMENTED 3 +#endif + +#ifndef EXIT_NOPERMISSION +#define EXIT_NOPERMISSION 4 +#endif + +#ifndef EXIT_NOTINSTALLED +#define EXIT_NOTINSTALLED 5 +#endif + +#ifndef EXIT_NOTCONFIGURED +#define EXIT_NOTCONFIGURED 6 +#endif + +#ifndef EXIT_NOTRUNNING +#define EXIT_NOTRUNNING 7 +#endif + /** * clang et al do not have such an attribute */ diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 6d5307235..a3a77073e 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -49,6 +49,7 @@ libgnunetutil_la_SOURCES = \ common_endian.c \ common_logging.c \ configuration.c \ + configuration_helper.c \ consttime_memcmp.c \ container_bloomfilter.c \ container_heap.c \ diff --git a/src/util/configuration_helper.c b/src/util/configuration_helper.c new file mode 100644 index 000000000..eb8b543d1 --- /dev/null +++ b/src/util/configuration_helper.c @@ -0,0 +1,316 @@ +/* + This file is part of GNUnet. + Copyright (C) 2006, 2007, 2008, 2009, 2013, 2020, 2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file src/util/configuration_helper.c + * @brief helper logic for gnunet-config + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" + +/** + * Print each option in a given section as a filename. + * + * @param cls closure + * @param section name of the section + * @param option name of the option + * @param value value of the option + */ +static void +print_filename_option (void *cls, + const char *section, + const char *option, + const char *value) +{ + const struct GNUNET_CONFIGURATION_Handle *cfg = cls; + + char *value_fn; + char *fn; + + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, + section, + option, + &value_fn)); + fn = GNUNET_STRINGS_filename_expand (value_fn); + if (NULL == fn) + fn = value_fn; + else + GNUNET_free (value_fn); + fprintf (stdout, + "%s = %s\n", + option, + fn); + GNUNET_free (fn); +} + + +/** + * Print each option in a given section. + * + * @param cls closure + * @param section name of the section + * @param option name of the option + * @param value value of the option + */ +static void +print_option (void *cls, + const char *section, + const char *option, + const char *value) +{ + (void) cls; + (void) section; + + fprintf (stdout, + "%s = %s\n", + option, + value); +} + + +/** + * Print out given section name. + * + * @param cls unused + * @param section a section in the configuration file + */ +static void +print_section_name (void *cls, + const char *section) +{ + (void) cls; + fprintf (stdout, + "%s\n", + section); +} + + +void +GNUNET_CONFIGURATION_config_tool_run ( + void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_CONFIGURATION_ConfigSettings *cs = cls; + struct GNUNET_CONFIGURATION_Handle *out = NULL; + struct GNUNET_CONFIGURATION_Handle *ncfg = NULL; + + (void) args; + if (NULL != cs->backend_check) + { + char *name; + + GNUNET_asprintf (&name, + "libgnunet_plugin_%s", + cs->backend_check); + cs->global_ret = (GNUNET_OK == + GNUNET_PLUGIN_test (name)) ? 0 : 77; + GNUNET_free (name); + return; + } + + if (cs->diagnostics) + { + /* Re-parse the configuration with diagnostics enabled. */ + ncfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_enable_diagnostics (ncfg); + GNUNET_CONFIGURATION_load (ncfg, + cfgfile); + cfg = ncfg; + } + + if (cs->full) + cs->rewrite = GNUNET_YES; + if (cs->list_sections) + { + fprintf (stderr, + _ ("The following sections are available:\n")); + GNUNET_CONFIGURATION_iterate_sections (cfg, + &print_section_name, + NULL); + return; + } + if ( (! cs->rewrite) && + (NULL == cs->section) ) + { + char *serialization; + + if (! cs->diagnostics) + { + fprintf (stderr, + _ ("%s, %s or %s argument is required\n"), + "--section", + "--list-sections", + "--diagnostics"); + cs->global_ret = EXIT_INVALIDARGUMENT; + return; + } + serialization = GNUNET_CONFIGURATION_serialize_diagnostics (cfg); + fprintf (stdout, + "%s", + serialization); + GNUNET_free (serialization); + } + else if ( (NULL != cs->section) && + (NULL == cs->value) ) + { + if (NULL == cs->option) + { + GNUNET_CONFIGURATION_iterate_section_values ( + cfg, + cs->section, + cs->is_filename + ? &print_filename_option + : &print_option, + (void *) cfg); + } + else + { + char *value; + + if (cs->is_filename) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + cs->section, + cs->option, + &value)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + cs->section, + cs->option); + cs->global_ret = EXIT_NOTCONFIGURED; + return; + } + } + else + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + cs->section, + cs->option, + &value)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + cs->section, + cs->option); + cs->global_ret = EXIT_NOTCONFIGURED; + return; + } + } + fprintf (stdout, + "%s\n", + value); + GNUNET_free (value); + } + } + else if (NULL != cs->section) + { + if (NULL == cs->option) + { + fprintf (stderr, + _ ("--option argument required to set value\n")); + cs->global_ret = EXIT_INVALIDARGUMENT; + return; + } + out = GNUNET_CONFIGURATION_dup (cfg); + GNUNET_CONFIGURATION_set_value_string (out, + cs->section, + cs->option, + cs->value); + cs->rewrite = GNUNET_YES; + } + if (cs->rewrite) + { + char *cfg_fn = NULL; + + if (NULL == out) + out = GNUNET_CONFIGURATION_dup (cfg); + + if (NULL == cfgfile) + { + const char *xdg = getenv ("XDG_CONFIG_HOME"); + + if (NULL != xdg) + GNUNET_asprintf (&cfg_fn, + "%s%s%s", + xdg, + DIR_SEPARATOR_STR, + GNUNET_OS_project_data_get ()->config_file); + else + cfg_fn = GNUNET_strdup ( + GNUNET_OS_project_data_get ()->user_config_file); + cfgfile = cfg_fn; + } + + if (! cs->full) + { + struct GNUNET_CONFIGURATION_Handle *def; + + def = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_load (def, + NULL)) + { + fprintf (stderr, + _ ("failed to load configuration defaults")); + cs->global_ret = 1; + GNUNET_CONFIGURATION_destroy (def); + GNUNET_CONFIGURATION_destroy (out); + GNUNET_free (cfg_fn); + return; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_write_diffs (def, + out, + cfgfile)) + cs->global_ret = 2; + GNUNET_CONFIGURATION_destroy (def); + } + else + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_write (out, + cfgfile)) + cs->global_ret = 2; + } + GNUNET_free (cfg_fn); + } + if (NULL != out) + GNUNET_CONFIGURATION_destroy (out); + if (NULL != ncfg) + GNUNET_CONFIGURATION_destroy (ncfg); +} + + +void +GNUNET_CONFIGURATION_config_settings_free ( + struct GNUNET_CONFIGURATION_ConfigSettings *cs) +{ + GNUNET_free (cs->option); + GNUNET_free (cs->section); + GNUNET_free (cs->value); + GNUNET_free (cs->backend_check); +} + + +/* end of configuration_helper.c */ diff --git a/src/util/gnunet-config.c b/src/util/gnunet-config.c index 807df0d74..202ef7866 100644 --- a/src/util/gnunet-config.c +++ b/src/util/gnunet-config.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2012 GNUnet e.V. + Copyright (C) 2012-2021 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -27,313 +27,6 @@ #include "gnunet_util_lib.h" -/** - * Name of the section - */ -static char *section; - -/** - * Name of the option - */ -static char *option; - -/** - * Value to set - */ -static char *value; - -/** - * Backend to check if the respective plugin is - * loadable. NULL if no check is to be performed. - * The value is the "basename" of the plugin to load. - */ -static char *backend_check; - -/** - * Treat option as a filename. - */ -static int is_filename; - -/** - * Whether to show the sections. - */ -static int list_sections; - -/** - * Return value from 'main'. - */ -static int global_ret; - -/** - * Should we write out the configuration file, even if no value was changed? - */ -static int rewrite; - -/** - * Should we give extra diagnostics? - */ -static int diagnostics; - - -/** - * Should the generated configuration file contain the whole configuration? - */ -static int full; - - -/** - * Print each option in a given section. - * - * @param cls closure - * @param section name of the section - * @param option name of the option - * @param value value of the option - */ -static void -print_option (void *cls, - const char *section, - const char *option, - const char *value) -{ - const struct GNUNET_CONFIGURATION_Handle *cfg = cls; - - (void) section; - if (is_filename) - { - char *value_fn; - char *fn; - - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_filename (cfg, - section, - option, - &value_fn)); - fn = GNUNET_STRINGS_filename_expand (value_fn); - if (NULL == fn) - fn = value_fn; - else - GNUNET_free (value_fn); - fprintf (stdout, "%s = %s\n", option, fn); - GNUNET_free (fn); - } - else - { - fprintf (stdout, "%s = %s\n", option, value); - } -} - - -/** - * Print out given section name. - * - * @param cls unused - * @param section a section in the configuration file - */ -static void -print_section_name (void *cls, const char *section) -{ - (void) cls; - fprintf (stdout, "%s\n", section); -} - - -/** - * Main function that will be run by the scheduler. - * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, - * can be NULL!) - * @param cfg configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_CONFIGURATION_Handle *out = NULL; - - (void) cls; - (void) args; - if (NULL != backend_check) - { - char *name; - - GNUNET_asprintf (&name, - "libgnunet_plugin_%s", - backend_check); - global_ret = (GNUNET_OK == - GNUNET_PLUGIN_test (name)) ? 0 : 77; - GNUNET_free (name); - return; - } - - if (diagnostics) - { - struct GNUNET_CONFIGURATION_Handle *ncfg; - /* Re-parse the configuration with diagnostics enabled. */ - ncfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_enable_diagnostics (ncfg); - GNUNET_CONFIGURATION_load (ncfg, cfgfile); - cfg = ncfg; - } - - if (full) - rewrite = GNUNET_YES; - if (list_sections) - { - fprintf (stderr, - _ ("The following sections are available:\n")); - GNUNET_CONFIGURATION_iterate_sections (cfg, - &print_section_name, - NULL); - return; - } - if ( (! rewrite) && - (NULL == section) ) - { - char *serialization; - - if (! diagnostics) - { - fprintf (stderr, - _ ("%s, %s or %s argument is required\n"), - "--section", - "--list-sections", - "--diagnostics"); - global_ret = 1; - return; - } - serialization = GNUNET_CONFIGURATION_serialize_diagnostics (cfg); - fprintf (stdout, - "%s", - serialization); - GNUNET_free (serialization); - } - else if ( (NULL != section) && - (NULL == value) ) - { - if (NULL == option) - { - GNUNET_CONFIGURATION_iterate_section_values (cfg, - section, - &print_option, - (void *) cfg); - } - else - { - if (is_filename) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - section, - option, - &value)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - option); - global_ret = 3; - return; - } - } - else - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - option, - &value)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - option); - global_ret = 3; - return; - } - } - fprintf (stdout, - "%s\n", - value); - } - } - else if (NULL != section) - { - if (NULL == option) - { - fprintf (stderr, - _ ("--option argument required to set value\n")); - global_ret = 1; - return; - } - out = GNUNET_CONFIGURATION_dup (cfg); - GNUNET_CONFIGURATION_set_value_string (out, - section, - option, - value); - rewrite = GNUNET_YES; - } - if (rewrite) - { - char *cfg_fn = NULL; - - if (NULL == out) - out = GNUNET_CONFIGURATION_dup (cfg); - - if (NULL == cfgfile) - { - const char *xdg = getenv ("XDG_CONFIG_HOME"); - if (NULL != xdg) - GNUNET_asprintf (&cfg_fn, - "%s%s%s", - xdg, - DIR_SEPARATOR_STR, - GNUNET_OS_project_data_get ()->config_file); - else - cfg_fn = GNUNET_strdup ( - GNUNET_OS_project_data_get ()->user_config_file); - cfgfile = cfg_fn; - } - - if (! full) - { - struct GNUNET_CONFIGURATION_Handle *def; - - def = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (def, - NULL)) - { - fprintf (stderr, - _ ("failed to load configuration defaults")); - global_ret = 1; - GNUNET_CONFIGURATION_destroy (def); - GNUNET_CONFIGURATION_destroy (out); - GNUNET_free (cfg_fn); - return; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_write_diffs (def, - out, - cfgfile)) - global_ret = 2; - GNUNET_CONFIGURATION_destroy (def); - } - else - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_write (out, - cfgfile)) - global_ret = 2; - } - GNUNET_free (cfg_fn); - } - if (NULL != out) - GNUNET_CONFIGURATION_destroy (out); -} - - /** * Program to manipulate configuration files. * @@ -342,82 +35,37 @@ run (void *cls, * @return 0 ok, 1 on error */ int -main (int argc, char *const *argv) +main (int argc, + char *const *argv) { + struct GNUNET_CONFIGURATION_ConfigSettings cs = { + .global_ret = EXIT_SUCCESS + }; struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_exclusive ( - GNUNET_GETOPT_option_string ( - 'b', - "supported-backend", - "BACKEND", - gettext_noop ( - "test if the current installation supports the specified BACKEND"), - &backend_check)), - GNUNET_GETOPT_option_flag ( - 'F', - "full", - gettext_noop ( - "write the full configuration file, including default values"), - &full), - GNUNET_GETOPT_option_flag ( - 'f', - "filename", - gettext_noop ("interpret option value as a filename (with $-expansion)"), - &is_filename), - GNUNET_GETOPT_option_string ('o', - "option", - "OPTION", - gettext_noop ("name of the option to access"), - &option), - GNUNET_GETOPT_option_flag ( - 'r', - "rewrite", - gettext_noop ( - "rewrite the configuration file, even if nothing changed"), - &rewrite), - GNUNET_GETOPT_option_flag ( - 'd', - "diagnostics", - gettext_noop ( - "output extra diagnostics"), - &diagnostics), - GNUNET_GETOPT_option_flag ('S', - "list-sections", - gettext_noop ( - "print available configuration sections"), - &list_sections), - GNUNET_GETOPT_option_string ('s', - "section", - "SECTION", - gettext_noop ( - "name of the section to access"), - §ion), - GNUNET_GETOPT_option_string ('V', - "value", - "VALUE", - gettext_noop ("value to set"), - &value), + GNUNET_CONFIGURATION_CONFIG_OPTIONS (&cs), GNUNET_GETOPT_OPTION_END }; - int ret; + enum GNUNET_GenericReturnValue ret; if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - + return EXIT_FAILURE; ret = GNUNET_PROGRAM_run (argc, argv, "gnunet-config [OPTIONS]", gettext_noop ("Manipulate GNUnet configuration files"), options, - &run, - NULL); + &GNUNET_CONFIGURATION_config_tool_run, + &cs); GNUNET_free_nz ((void *) argv); - if (GNUNET_OK == ret) - return global_ret; - return ret; + GNUNET_CONFIGURATION_config_settings_free (&cs); + if (GNUNET_NO == ret) + return 0; + if (GNUNET_SYSERR == ret) + return EXIT_INVALIDARGUMENT; + return cs.global_ret; } -- cgit v1.2.3