From: Lennart Poettering Date: Mon, 21 Feb 2022 13:39:16 +0000 (+0100) Subject: analyze: split out "verify" verb into own .c/.h file X-Git-Tag: v251-rc1~249^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f2562398ea50c18decab166dfab61b4d09373eaf;p=thirdparty%2Fsystemd.git analyze: split out "verify" verb into own .c/.h file This renames the old analyze-verify.[ch] pair → analyze-verify-util.[ch], because it's used by the test logic as well, and by keeping it separate from the verify verb logic we don't have to import the arg_xyz variables. --- diff --git a/src/analyze/analyze-condition.c b/src/analyze/analyze-condition.c index f57bb27b9a8..e8b0fa978bc 100644 --- a/src/analyze/analyze-condition.c +++ b/src/analyze/analyze-condition.c @@ -3,7 +3,7 @@ #include #include "analyze-condition.h" -#include "analyze-verify.h" +#include "analyze-verify-util.h" #include "condition.h" #include "conf-parser.h" #include "load-fragment.h" diff --git a/src/analyze/analyze-verify-util.c b/src/analyze/analyze-verify-util.c new file mode 100644 index 00000000000..6c28cc0ca9d --- /dev/null +++ b/src/analyze/analyze-verify-util.c @@ -0,0 +1,351 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "all-units.h" +#include "alloc-util.h" +#include "analyze-verify-util.h" +#include "bus-error.h" +#include "bus-util.h" +#include "log.h" +#include "manager.h" +#include "pager.h" +#include "path-util.h" +#include "string-table.h" +#include "strv.h" +#include "unit-name.h" +#include "unit-serialize.h" + +static void log_syntax_callback(const char *unit, int level, void *userdata) { + Set **s = userdata; + int r; + + assert(userdata); + assert(unit); + + if (level > LOG_WARNING) + return; + + if (*s == POINTER_MAX) + return; + + r = set_put_strdup(s, unit); + if (r < 0) { + set_free_free(*s); + *s = POINTER_MAX; + } +} + +int verify_prepare_filename(const char *filename, char **ret) { + int r; + const char *name; + _cleanup_free_ char *abspath = NULL; + _cleanup_free_ char *dir = NULL; + _cleanup_free_ char *with_instance = NULL; + char *c; + + assert(filename); + assert(ret); + + r = path_make_absolute_cwd(filename, &abspath); + if (r < 0) + return r; + + name = basename(abspath); + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { + r = unit_name_replace_instance(name, "i", &with_instance); + if (r < 0) + return r; + } + + dir = dirname_malloc(abspath); + if (!dir) + return -ENOMEM; + + c = path_join(dir, with_instance ?: name); + if (!c) + return -ENOMEM; + + *ret = c; + return 0; +} + +int verify_generate_path(char **var, char **filenames) { + const char *old; + char **filename; + + _cleanup_strv_free_ char **ans = NULL; + int r; + + STRV_FOREACH(filename, filenames) { + char *t; + + t = dirname_malloc(*filename); + if (!t) + return -ENOMEM; + + r = strv_consume(&ans, t); + if (r < 0) + return r; + } + + assert_se(strv_uniq(ans)); + + /* First, prepend our directories. Second, if some path was specified, use that, and + * otherwise use the defaults. Any duplicates will be filtered out in path-lookup.c. + * Treat explicit empty path to mean that nothing should be appended. + */ + old = getenv("SYSTEMD_UNIT_PATH"); + if (!streq_ptr(old, "")) { + if (!old) + old = ":"; + + r = strv_extend(&ans, old); + if (r < 0) + return r; + } + + *var = strv_join(ans, ":"); + if (!*var) + return -ENOMEM; + + return 0; +} + +static int verify_socket(Unit *u) { + Unit *service; + int r; + + assert(u); + + if (u->type != UNIT_SOCKET) + return 0; + + r = socket_load_service_unit(SOCKET(u), -1, &service); + if (r < 0) + return log_unit_error_errno(u, r, "service unit for the socket cannot be loaded: %m"); + + if (service->load_state != UNIT_LOADED) + return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), + "service %s not loaded, socket cannot be started.", service->id); + + log_unit_debug(u, "using service unit %s.", service->id); + return 0; +} + +int verify_executable(Unit *u, const ExecCommand *exec, const char *root) { + int r; + + if (!exec) + return 0; + + if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE) + return 0; + + r = find_executable_full(exec->path, root, NULL, false, NULL, NULL); + if (r < 0) + return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path); + + return 0; +} + +static int verify_executables(Unit *u, const char *root) { + ExecCommand *exec; + int r = 0, k; + unsigned i; + + assert(u); + + exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command : + u->type == UNIT_MOUNT ? MOUNT(u)->control_command : + u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL; + k = verify_executable(u, exec, root); + if (k < 0 && r == 0) + r = k; + + if (u->type == UNIT_SERVICE) + for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) { + k = verify_executable(u, SERVICE(u)->exec_command[i], root); + if (k < 0 && r == 0) + r = k; + } + + if (u->type == UNIT_SOCKET) + for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) { + k = verify_executable(u, SOCKET(u)->exec_command[i], root); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static int verify_documentation(Unit *u, bool check_man) { + char **p; + int r = 0, k; + + STRV_FOREACH(p, u->documentation) { + log_unit_debug(u, "Found documentation item: %s", *p); + + if (check_man && startswith(*p, "man:")) { + k = show_man_page(*p + 4, true); + if (k != 0) { + if (k < 0) + log_unit_error_errno(u, k, "Can't show %s: %m", *p + 4); + else { + log_unit_error(u, "Command 'man %s' failed with code %d", *p + 4, k); + k = -ENOEXEC; + } + if (r == 0) + r = k; + } + } + } + + /* Check remote URLs? */ + + return r; +} + +static int verify_unit(Unit *u, bool check_man, const char *root) { + _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; + int r, k; + + assert(u); + + if (DEBUG_LOGGING) + unit_dump(u, stdout, "\t"); + + log_unit_debug(u, "Creating %s/start job", u->id); + r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, NULL, &err, NULL); + if (r < 0) + log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r)); + + k = verify_socket(u); + if (k < 0 && r == 0) + r = k; + + k = verify_executables(u, root); + if (k < 0 && r == 0) + r = k; + + k = verify_documentation(u, check_man); + if (k < 0 && r == 0) + r = k; + + return r; +} + +static void set_destroy_ignore_pointer_max(Set** s) { + if (*s == POINTER_MAX) + return; + set_free_free(*s); +} + +int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) { + const ManagerTestRunFlags flags = + MANAGER_TEST_RUN_MINIMAL | + MANAGER_TEST_RUN_ENV_GENERATORS | + (recursive_errors == RECURSIVE_ERRORS_NO) * MANAGER_TEST_RUN_IGNORE_DEPENDENCIES | + run_generators * MANAGER_TEST_RUN_GENERATORS; + + _cleanup_(manager_freep) Manager *m = NULL; + _cleanup_(set_destroy_ignore_pointer_max) Set *s = NULL; + _unused_ _cleanup_(clear_log_syntax_callback) dummy_t dummy; + Unit *units[strv_length(filenames)]; + _cleanup_free_ char *var = NULL; + int r, k, i, count = 0; + char **filename; + + if (strv_isempty(filenames)) + return 0; + + /* Allow systemd-analyze to hook in a callback function so that it can get + * all the required log data from the function itself without having to rely + * on a global set variable for the same */ + set_log_syntax_callback(log_syntax_callback, &s); + + /* set the path */ + r = verify_generate_path(&var, filenames); + if (r < 0) + return log_error_errno(r, "Failed to generate unit load path: %m"); + + assert_se(set_unit_path(var) >= 0); + + r = manager_new(scope, flags, &m); + if (r < 0) + return log_error_errno(r, "Failed to initialize manager: %m"); + + log_debug("Starting manager..."); + + r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root); + if (r < 0) + return r; + + manager_clear_jobs(m); + + log_debug("Loading remaining units from the command line..."); + + STRV_FOREACH(filename, filenames) { + _cleanup_free_ char *prepared = NULL; + + log_debug("Handling %s...", *filename); + + k = verify_prepare_filename(*filename, &prepared); + if (k < 0) { + log_error_errno(k, "Failed to prepare filename %s: %m", *filename); + if (r == 0) + r = k; + continue; + } + + k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]); + if (k < 0) { + if (r == 0) + r = k; + continue; + } + + count++; + } + + for (i = 0; i < count; i++) { + k = verify_unit(units[i], check_man, root); + if (k < 0 && r == 0) + r = k; + } + + if (s == POINTER_MAX) + return log_oom(); + + if (set_isempty(s) || r != 0) + return r; + + /* If all previous verifications succeeded, then either the recursive parsing of all the + * associated dependencies with RECURSIVE_ERRORS_YES or the parsing of the specified unit file + * with RECURSIVE_ERRORS_NO must have yielded a syntax warning and hence, a non-empty set. */ + if (IN_SET(recursive_errors, RECURSIVE_ERRORS_YES, RECURSIVE_ERRORS_NO)) + return -ENOTRECOVERABLE; + + /* If all previous verifications succeeded, then the non-empty set could have resulted from + * a syntax warning encountered during the recursive parsing of the specified unit file and + * its direct dependencies. Hence, search for any of the filenames in the set and if found, + * return a non-zero process exit status. */ + if (recursive_errors == RECURSIVE_ERRORS_ONE) + STRV_FOREACH(filename, filenames) + if (set_contains(s, basename(*filename))) + return -ENOTRECOVERABLE; + + return 0; +} + +static const char* const recursive_errors_table[_RECURSIVE_ERRORS_MAX] = { + [RECURSIVE_ERRORS_NO] = "no", + [RECURSIVE_ERRORS_YES] = "yes", + [RECURSIVE_ERRORS_ONE] = "one", +}; + +DEFINE_STRING_TABLE_LOOKUP(recursive_errors, RecursiveErrors); diff --git a/src/analyze/analyze-verify-util.h b/src/analyze/analyze-verify-util.h new file mode 100644 index 00000000000..47b78a81589 --- /dev/null +++ b/src/analyze/analyze-verify-util.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "execute.h" +#include "path-lookup.h" + +typedef enum RecursiveErrors { + RECURSIVE_ERRORS_YES, /* Look for errors in all associated units */ + RECURSIVE_ERRORS_NO, /* Don't look for errors in any but the selected unit */ + RECURSIVE_ERRORS_ONE, /* Look for errors in the selected unit and its direct dependencies */ + _RECURSIVE_ERRORS_MAX, + _RECURSIVE_ERRORS_INVALID = -EINVAL, +} RecursiveErrors; + +int verify_generate_path(char **var, char **filenames); +int verify_prepare_filename(const char *filename, char **ret); +int verify_executable(Unit *u, const ExecCommand *exec, const char *root); +int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root); + +const char* recursive_errors_to_string(RecursiveErrors i) _const_; +RecursiveErrors recursive_errors_from_string(const char *s) _pure_; diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 943a1f27de9..05fd8a6336f 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -1,351 +1,71 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include - -#include "alloc-util.h" -#include "all-units.h" +#include "analyze.h" #include "analyze-verify.h" -#include "bus-error.h" -#include "bus-util.h" -#include "log.h" -#include "manager.h" -#include "pager.h" -#include "path-util.h" -#include "string-table.h" -#include "strv.h" -#include "unit-name.h" -#include "unit-serialize.h" - -static void log_syntax_callback(const char *unit, int level, void *userdata) { - Set **s = userdata; - int r; - - assert(userdata); - assert(unit); +#include "analyze-verify-util.h" +#include "copy.h" +#include "rm-rf.h" +#include "tmpfile-util.h" - if (level > LOG_WARNING) - return; - - if (*s == POINTER_MAX) - return; - - r = set_put_strdup(s, unit); - if (r < 0) { - set_free_free(*s); - *s = POINTER_MAX; - } -} - -int verify_prepare_filename(const char *filename, char **ret) { +static int process_aliases(char *argv[], char *tempdir, char ***ret) { + _cleanup_strv_free_ char **filenames = NULL; + char **filename; int r; - const char *name; - _cleanup_free_ char *abspath = NULL; - _cleanup_free_ char *dir = NULL; - _cleanup_free_ char *with_instance = NULL; - char *c; - assert(filename); + assert(argv); + assert(tempdir); assert(ret); - r = path_make_absolute_cwd(filename, &abspath); - if (r < 0) - return r; - - name = basename(abspath); - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; + STRV_FOREACH(filename, strv_skip(argv, 1)) { + _cleanup_free_ char *src = NULL, *dst = NULL, *base = NULL; + const char *parse_arg; - if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { - r = unit_name_replace_instance(name, "i", &with_instance); + parse_arg = *filename; + r = extract_first_word(&parse_arg, &src, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE); if (r < 0) return r; - } - dir = dirname_malloc(abspath); - if (!dir) - return -ENOMEM; + if (!parse_arg) { + r = strv_consume(&filenames, TAKE_PTR(src)); + if (r < 0) + return r; - c = path_join(dir, with_instance ?: name); - if (!c) - return -ENOMEM; - - *ret = c; - return 0; -} - -int verify_generate_path(char **var, char **filenames) { - const char *old; - char **filename; - - _cleanup_strv_free_ char **ans = NULL; - int r; + continue; + } - STRV_FOREACH(filename, filenames) { - char *t; + r = path_extract_filename(parse_arg, &base); + if (r < 0) + return r; - t = dirname_malloc(*filename); - if (!t) + dst = path_join(tempdir, base); + if (!dst) return -ENOMEM; - r = strv_consume(&ans, t); + r = copy_file(src, dst, 0, 0644, 0, 0, COPY_REFLINK); if (r < 0) return r; - } - - assert_se(strv_uniq(ans)); - /* First, prepend our directories. Second, if some path was specified, use that, and - * otherwise use the defaults. Any duplicates will be filtered out in path-lookup.c. - * Treat explicit empty path to mean that nothing should be appended. - */ - old = getenv("SYSTEMD_UNIT_PATH"); - if (!streq_ptr(old, "")) { - if (!old) - old = ":"; - - r = strv_extend(&ans, old); + r = strv_consume(&filenames, TAKE_PTR(dst)); if (r < 0) return r; } - *var = strv_join(ans, ":"); - if (!*var) - return -ENOMEM; - + *ret = TAKE_PTR(filenames); return 0; } -static int verify_socket(Unit *u) { - Unit *service; +int do_verify(int argc, char *argv[], void *userdata) { + _cleanup_strv_free_ char **filenames = NULL; + _cleanup_(rm_rf_physical_and_freep) char *tempdir = NULL; int r; - assert(u); - - if (u->type != UNIT_SOCKET) - return 0; - - r = socket_load_service_unit(SOCKET(u), -1, &service); - if (r < 0) - return log_unit_error_errno(u, r, "service unit for the socket cannot be loaded: %m"); - - if (service->load_state != UNIT_LOADED) - return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), - "service %s not loaded, socket cannot be started.", service->id); - - log_unit_debug(u, "using service unit %s.", service->id); - return 0; -} - -int verify_executable(Unit *u, const ExecCommand *exec, const char *root) { - int r; - - if (!exec) - return 0; - - if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE) - return 0; - - r = find_executable_full(exec->path, root, NULL, false, NULL, NULL); - if (r < 0) - return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path); - - return 0; -} - -static int verify_executables(Unit *u, const char *root) { - ExecCommand *exec; - int r = 0, k; - unsigned i; - - assert(u); - - exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command : - u->type == UNIT_MOUNT ? MOUNT(u)->control_command : - u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL; - k = verify_executable(u, exec, root); - if (k < 0 && r == 0) - r = k; - - if (u->type == UNIT_SERVICE) - for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) { - k = verify_executable(u, SERVICE(u)->exec_command[i], root); - if (k < 0 && r == 0) - r = k; - } - - if (u->type == UNIT_SOCKET) - for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) { - k = verify_executable(u, SOCKET(u)->exec_command[i], root); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static int verify_documentation(Unit *u, bool check_man) { - char **p; - int r = 0, k; - - STRV_FOREACH(p, u->documentation) { - log_unit_debug(u, "Found documentation item: %s", *p); - - if (check_man && startswith(*p, "man:")) { - k = show_man_page(*p + 4, true); - if (k != 0) { - if (k < 0) - log_unit_error_errno(u, k, "Can't show %s: %m", *p + 4); - else { - log_unit_error(u, "Command 'man %s' failed with code %d", *p + 4, k); - k = -ENOEXEC; - } - if (r == 0) - r = k; - } - } - } - - /* Check remote URLs? */ - - return r; -} - -static int verify_unit(Unit *u, bool check_man, const char *root) { - _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; - int r, k; - - assert(u); - - if (DEBUG_LOGGING) - unit_dump(u, stdout, "\t"); - - log_unit_debug(u, "Creating %s/start job", u->id); - r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, NULL, &err, NULL); - if (r < 0) - log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r)); - - k = verify_socket(u); - if (k < 0 && r == 0) - r = k; - - k = verify_executables(u, root); - if (k < 0 && r == 0) - r = k; - - k = verify_documentation(u, check_man); - if (k < 0 && r == 0) - r = k; - - return r; -} - -static void set_destroy_ignore_pointer_max(Set** s) { - if (*s == POINTER_MAX) - return; - set_free_free(*s); -} - -int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) { - const ManagerTestRunFlags flags = - MANAGER_TEST_RUN_MINIMAL | - MANAGER_TEST_RUN_ENV_GENERATORS | - (recursive_errors == RECURSIVE_ERRORS_NO) * MANAGER_TEST_RUN_IGNORE_DEPENDENCIES | - run_generators * MANAGER_TEST_RUN_GENERATORS; - - _cleanup_(manager_freep) Manager *m = NULL; - _cleanup_(set_destroy_ignore_pointer_max) Set *s = NULL; - _unused_ _cleanup_(clear_log_syntax_callback) dummy_t dummy; - Unit *units[strv_length(filenames)]; - _cleanup_free_ char *var = NULL; - int r, k, i, count = 0; - char **filename; - - if (strv_isempty(filenames)) - return 0; - - /* Allow systemd-analyze to hook in a callback function so that it can get - * all the required log data from the function itself without having to rely - * on a global set variable for the same */ - set_log_syntax_callback(log_syntax_callback, &s); - - /* set the path */ - r = verify_generate_path(&var, filenames); + r = mkdtemp_malloc("/tmp/systemd-analyze-XXXXXX", &tempdir); if (r < 0) - return log_error_errno(r, "Failed to generate unit load path: %m"); - - assert_se(set_unit_path(var) >= 0); + return log_error_errno(r, "Failed to setup working directory: %m"); - r = manager_new(scope, flags, &m); + r = process_aliases(argv, tempdir, &filenames); if (r < 0) - return log_error_errno(r, "Failed to initialize manager: %m"); - - log_debug("Starting manager..."); - - r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root); - if (r < 0) - return r; - - manager_clear_jobs(m); - - log_debug("Loading remaining units from the command line..."); - - STRV_FOREACH(filename, filenames) { - _cleanup_free_ char *prepared = NULL; - - log_debug("Handling %s...", *filename); - - k = verify_prepare_filename(*filename, &prepared); - if (k < 0) { - log_error_errno(k, "Failed to prepare filename %s: %m", *filename); - if (r == 0) - r = k; - continue; - } - - k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]); - if (k < 0) { - if (r == 0) - r = k; - continue; - } - - count++; - } - - for (i = 0; i < count; i++) { - k = verify_unit(units[i], check_man, root); - if (k < 0 && r == 0) - r = k; - } - - if (s == POINTER_MAX) - return log_oom(); + return log_error_errno(r, "Couldn't process aliases: %m"); - if (set_isempty(s) || r != 0) - return r; - - /* If all previous verifications succeeded, then either the recursive parsing of all the - * associated dependencies with RECURSIVE_ERRORS_YES or the parsing of the specified unit file - * with RECURSIVE_ERRORS_NO must have yielded a syntax warning and hence, a non-empty set. */ - if (IN_SET(recursive_errors, RECURSIVE_ERRORS_YES, RECURSIVE_ERRORS_NO)) - return -ENOTRECOVERABLE; - - /* If all previous verifications succeeded, then the non-empty set could have resulted from - * a syntax warning encountered during the recursive parsing of the specified unit file and - * its direct dependencies. Hence, search for any of the filenames in the set and if found, - * return a non-zero process exit status. */ - if (recursive_errors == RECURSIVE_ERRORS_ONE) - STRV_FOREACH(filename, filenames) - if (set_contains(s, basename(*filename))) - return -ENOTRECOVERABLE; - - return 0; + return verify_units(filenames, arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root); } - -static const char* const recursive_errors_table[_RECURSIVE_ERRORS_MAX] = { - [RECURSIVE_ERRORS_NO] = "no", - [RECURSIVE_ERRORS_YES] = "yes", - [RECURSIVE_ERRORS_ONE] = "one", -}; - -DEFINE_STRING_TABLE_LOOKUP(recursive_errors, RecursiveErrors); diff --git a/src/analyze/analyze-verify.h b/src/analyze/analyze-verify.h index 47b78a81589..1193a0e38c3 100644 --- a/src/analyze/analyze-verify.h +++ b/src/analyze/analyze-verify.h @@ -1,23 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include - -#include "execute.h" -#include "path-lookup.h" - -typedef enum RecursiveErrors { - RECURSIVE_ERRORS_YES, /* Look for errors in all associated units */ - RECURSIVE_ERRORS_NO, /* Don't look for errors in any but the selected unit */ - RECURSIVE_ERRORS_ONE, /* Look for errors in the selected unit and its direct dependencies */ - _RECURSIVE_ERRORS_MAX, - _RECURSIVE_ERRORS_INVALID = -EINVAL, -} RecursiveErrors; - -int verify_generate_path(char **var, char **filenames); -int verify_prepare_filename(const char *filename, char **ret); -int verify_executable(Unit *u, const ExecCommand *exec, const char *root); -int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root); - -const char* recursive_errors_to_string(RecursiveErrors i) _const_; -RecursiveErrors recursive_errors_from_string(const char *s) _pure_; +int do_verify(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 68cda2b5ea7..cb4d8c7ca47 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -90,7 +90,7 @@ PagerFlags arg_pager_flags = 0; BusTransport arg_transport = BUS_TRANSPORT_LOCAL; const char *arg_host = NULL; UnitFileScope arg_scope = UNIT_FILE_SYSTEM; -static RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES; +RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES; bool arg_man = true; bool arg_generators = false; char *arg_root = NULL; @@ -151,53 +151,6 @@ int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *proper return 0; } -static int process_aliases(char *argv[], char *tempdir, char ***ret) { - _cleanup_strv_free_ char **filenames = NULL; - char **filename; - int r; - - assert(argv); - assert(tempdir); - assert(ret); - - STRV_FOREACH(filename, strv_skip(argv, 1)) { - _cleanup_free_ char *src = NULL, *dst = NULL, *base = NULL; - const char *parse_arg; - - parse_arg = *filename; - r = extract_first_word(&parse_arg, &src, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE); - if (r < 0) - return r; - - if (!parse_arg) { - r = strv_consume(&filenames, TAKE_PTR(src)); - if (r < 0) - return r; - - continue; - } - - r = path_extract_filename(parse_arg, &base); - if (r < 0) - return r; - - dst = path_join(tempdir, base); - if (!dst) - return -ENOMEM; - - r = copy_file(src, dst, 0, 0644, 0, 0, COPY_REFLINK); - if (r < 0) - return r; - - r = strv_consume(&filenames, TAKE_PTR(dst)); - if (r < 0) - return r; - } - - *ret = TAKE_PTR(filenames); - return 0; -} - void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) { if (calendar && calendar_spec_from_string(p, NULL) >= 0) log_notice("Hint: this expression is a valid calendar specification. " @@ -214,22 +167,6 @@ static int do_condition(int argc, char *argv[], void *userdata) { return verify_conditions(strv_skip(argv, 1), arg_scope, arg_unit, arg_root); } -static int do_verify(int argc, char *argv[], void *userdata) { - _cleanup_strv_free_ char **filenames = NULL; - _cleanup_(rm_rf_physical_and_freep) char *tempdir = NULL; - int r; - - r = mkdtemp_malloc("/tmp/systemd-analyze-XXXXXX", &tempdir); - if (r < 0) - return log_error_errno(r, "Failed to setup working directory: %m"); - - r = process_aliases(argv, tempdir, &filenames); - if (r < 0) - return log_error_errno(r, "Couldn't process aliases: %m"); - - return verify_units(filenames, arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root); -} - static int help(int argc, char *argv[], void *userdata) { _cleanup_free_ char *link = NULL, *dot_link = NULL; int r; diff --git a/src/analyze/analyze.h b/src/analyze/analyze.h index 94eec410621..9755f9df36d 100644 --- a/src/analyze/analyze.h +++ b/src/analyze/analyze.h @@ -3,6 +3,7 @@ #include +#include "analyze-verify-util.h" #include "bus-util.h" #include "json.h" #include "pager.h" @@ -22,6 +23,7 @@ extern PagerFlags arg_pager_flags; extern BusTransport arg_transport; extern const char *arg_host; extern UnitFileScope arg_scope; +extern RecursiveErrors arg_recursive_errors; extern bool arg_man; extern bool arg_generators; extern char *arg_root; diff --git a/src/analyze/meson.build b/src/analyze/meson.build index 83b244c80c2..619f878e546 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -47,13 +47,15 @@ systemd_analyze_sources = files(''' analyze-unit-paths.h analyze-verify.c analyze-verify.h + analyze-verify-util.c + analyze-verify-util.h analyze.c '''.split()) tests += [ [files('test-verify.c', - 'analyze-verify.c', - 'analyze-verify.h'), + 'analyze-verify-util.c', + 'analyze-verify-util.h'), [libcore, libshared], [], diff --git a/src/analyze/test-verify.c b/src/analyze/test-verify.c index eaf5e0b39b4..f8c0bd97869 100644 --- a/src/analyze/test-verify.c +++ b/src/analyze/test-verify.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "analyze-verify.h" + +#include "analyze-verify-util.h" #include "tests.h" static void test_verify_nonexistent(void) {