diff --git a/src/basic/meson.build b/src/basic/meson.build index 69c525dcdc..0b2106807c 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -131,6 +131,8 @@ basic_sources = files(''' ordered-set.h pager.c pager.h + os-util.c + os-util.h parse-util.c parse-util.h path-util.c diff --git a/src/basic/os-util.c b/src/basic/os-util.c new file mode 100644 index 0000000000..207594cef8 --- /dev/null +++ b/src/basic/os-util.c @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "fd-util.h" +#include "fs-util.h" +#include "macro.h" +#include "os-util.h" +#include "strv.h" +#include "fileio.h" +#include "string-util.h" + +int path_is_os_tree(const char *path) { + int r; + + assert(path); + + /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir + * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from + * the case where just the os-release file is missing. */ + if (laccess(path, F_OK) < 0) + return -errno; + + /* We use {/etc|/usr/lib}/os-release as flag file if something is an OS */ + r = open_os_release(path, NULL, NULL); + if (r == -ENOENT) /* We got nothing */ + return 0; + if (r < 0) + return r; + + return 1; +} + +int open_os_release(const char *root, char **ret_path, int *ret_fd) { + _cleanup_free_ char *q = NULL; + const char *p; + int k; + + FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") { + k = chase_symlinks(p, root, CHASE_PREFIX_ROOT|(ret_fd ? CHASE_OPEN : 0), (ret_path ? &q : NULL)); + if (k != -ENOENT) + break; + } + if (k < 0) + return k; + + if (ret_fd) { + int real_fd; + + /* Convert the O_PATH fd into a proper, readable one */ + real_fd = fd_reopen(k, O_RDONLY|O_CLOEXEC|O_NOCTTY); + safe_close(k); + if (real_fd < 0) + return real_fd; + + *ret_fd = real_fd; + } + + if (ret_path) + *ret_path = TAKE_PTR(q); + + return 0; +} + +int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) { + _cleanup_free_ char *p = NULL; + _cleanup_close_ int fd = -1; + FILE *f; + int r; + + if (!ret_file) + return open_os_release(root, ret_path, NULL); + + r = open_os_release(root, ret_path ? &p : NULL, &fd); + if (r < 0) + return r; + + f = fdopen(fd, "re"); + if (!f) + return -errno; + fd = -1; + + *ret_file = f; + + if (ret_path) + *ret_path = TAKE_PTR(p); + + return 0; +} + +int parse_os_release(const char *root, ...) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; + va_list ap; + int r; + + r = fopen_os_release(root, &p, &f); + if (r < 0) + return r; + + va_start(ap, root); + r = parse_env_filev(f, p, NEWLINE, ap); + va_end(ap); + + return r; +} + +int load_os_release_pairs(const char *root, char ***ret) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; + int r; + + r = fopen_os_release(root, &p, &f); + if (r < 0) + return r; + + return load_env_file_pairs(f, p, NEWLINE, ret); +} diff --git a/src/basic/os-util.h b/src/basic/os-util.h new file mode 100644 index 0000000000..6b9e033941 --- /dev/null +++ b/src/basic/os-util.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +int path_is_os_tree(const char *path); + +int open_os_release(const char *root, char **ret_path, int *ret_fd); +int fopen_os_release(const char *root, char **ret_path, FILE **ret_file); + +int parse_os_release(const char *root, ...); +int load_os_release_pairs(const char *root, char ***ret); diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 61cc6f5738..1f0a75a0e8 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -132,32 +132,6 @@ int path_is_read_only_fs(const char *path) { return false; } -int path_is_os_tree(const char *path) { - int r; - - assert(path); - - /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir - * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from - * the case where just the os-release file is missing. */ - if (laccess(path, F_OK) < 0) - return -errno; - - /* We use /usr/lib/os-release as flag file if something is an OS */ - r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL); - if (r == -ENOENT) { - - /* Also check for the old location in /etc, just in case. */ - r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL); - if (r == -ENOENT) - return 0; /* We got nothing */ - } - if (r < 0) - return r; - - return 1; -} - int files_same(const char *filea, const char *fileb, int flags) { struct stat a, b; diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index d1e8d33001..02b1c47123 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -35,7 +35,6 @@ int null_or_empty_path(const char *fn); int null_or_empty_fd(int fd); int path_is_read_only_fs(const char *path); -int path_is_os_tree(const char *path); int files_same(const char *filea, const char *fileb, int flags); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 13d7bd476f..f7535e38fc 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -26,6 +26,7 @@ #include "fs-util.h" #include "install.h" #include "log.h" +#include "os-util.h" #include "parse-util.h" #include "path-util.h" #include "selinux-access.h" diff --git a/src/core/main.c b/src/core/main.c index e7d89be3f4..7e5a7c22b7 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -57,6 +57,7 @@ #include "manager.h" #include "missing.h" #include "mount-setup.h" +#include "os-util.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" @@ -1215,23 +1216,18 @@ static int enforce_syscall_archs(Set *archs) { static int status_welcome(void) { _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL; - const char *fn; int r; if (arg_show_status <= 0) return 0; - FOREACH_STRING(fn, "/etc/os-release", "/usr/lib/os-release") { - r = parse_env_file(NULL, fn, NEWLINE, - "PRETTY_NAME", &pretty_name, - "ANSI_COLOR", &ansi_color, - NULL); - - if (r != -ENOENT) - break; - } - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read os-release file, ignoring: %m"); + r = parse_os_release(NULL, + "PRETTY_NAME", &pretty_name, + "ANSI_COLOR", &ansi_color, + NULL); + if (r < 0) + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read os-release file, ignoring: %m"); if (log_get_show_color()) return status_printf(NULL, false, false, diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 9b091bf87a..0fc8bb891b 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -33,6 +33,7 @@ #include "hostname-util.h" #include "locale-util.h" #include "mkdir.h" +#include "os-util.h" #include "parse-util.h" #include "path-util.h" #include "proc-cmdline.h" @@ -79,27 +80,16 @@ static bool press_any_key(void) { static void print_welcome(void) { _cleanup_free_ char *pretty_name = NULL; - const char *os_release = NULL; static bool done = false; int r; if (done) return; - os_release = prefix_roota(arg_root, "/etc/os-release"); - r = parse_env_file(NULL, os_release, NEWLINE, - "PRETTY_NAME", &pretty_name, - NULL); - if (r == -ENOENT) { - - os_release = prefix_roota(arg_root, "/usr/lib/os-release"); - r = parse_env_file(NULL, os_release, NEWLINE, - "PRETTY_NAME", &pretty_name, - NULL); - } - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read os-release file: %m"); + r = parse_os_release(arg_root, "PRETTY_NAME", &pretty_name, NULL); + if (r < 0) + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read os-release file, ignoring: %m"); printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n", isempty(pretty_name) ? "Linux" : pretty_name); diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index db54e472c6..28fc628742 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -16,6 +16,7 @@ #include "env-util.h" #include "fileio-label.h" #include "hostname-util.h" +#include "os-util.h" #include "parse-util.h" #include "path-util.h" #include "selinux-util.h" @@ -98,18 +99,11 @@ static int context_read_data(Context *c) { if (r < 0 && r != -ENOENT) return r; - r = parse_env_file(NULL, "/etc/os-release", NEWLINE, - "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], - "CPE_NAME", &c->data[PROP_OS_CPE_NAME], - "HOME_URL", &c->data[PROP_HOME_URL], - NULL); - if (r == -ENOENT) - r = parse_env_file(NULL, "/usr/lib/os-release", NEWLINE, - "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], - "CPE_NAME", &c->data[PROP_OS_CPE_NAME], - "HOME_URL", &c->data[PROP_HOME_URL], - NULL); - + r = parse_os_release(NULL, + "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], + "CPE_NAME", &c->data[PROP_OS_CPE_NAME], + "HOME_URL", &c->data[PROP_HOME_URL], + NULL); if (r < 0 && r != -ENOENT) return r; diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index f80ca7e671..c29b65764a 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -24,6 +24,7 @@ #include "log.h" #include "logs-show.h" #include "microhttpd-util.h" +#include "os-util.h" #include "parse-util.h" #include "sigbus.h" #include "util.h" @@ -777,10 +778,8 @@ static int request_handler_machine( if (r < 0) return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m"); - if (parse_env_file(NULL, "/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL) == -ENOENT) - (void) parse_env_file(NULL, "/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL); - - get_virtualization(&v); + (void) parse_os_release(NULL, "PRETTY_NAME", &os_name, NULL); + (void) get_virtualization(&v); r = asprintf(&json, "{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\"," diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 058543fbb2..d7921cf8cf 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -32,6 +32,7 @@ #include "machine-dbus.h" #include "machine.h" #include "mkdir.h" +#include "os-util.h" #include "path-util.h" #include "process-util.h" #include "signal-util.h" @@ -330,7 +331,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s switch (m->class) { case MACHINE_HOST: - r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l); + r = load_os_release_pairs(NULL, &l); if (r < 0) return r; @@ -361,13 +362,10 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s if (r < 0) _exit(EXIT_FAILURE); - fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0 && errno == ENOENT) { - fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0 && errno == ENOENT) - _exit(EXIT_NOT_FOUND); - } - if (fd < 0) + r = open_os_release(NULL, NULL, &fd); + if (r == -ENOENT) + _exit(EXIT_NOT_FOUND); + if (r < 0) _exit(EXIT_FAILURE); r = copy_bytes(fd, pair[1], (uint64_t) -1, 0); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 56c26aae8e..4c157cf654 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -77,6 +77,7 @@ #include "nspawn-settings.h" #include "nspawn-setuid.h" #include "nspawn-stub-pid1.h" +#include "os-util.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 608e4b51ac..59ee4fe9e5 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -30,6 +30,7 @@ #include "linux-3.13/dm-ioctl.h" #include "missing.h" #include "mount-util.h" +#include "os-util.h" #include "path-util.h" #include "process-util.h" #include "raw-clone.h" diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 440b1f574f..44cb371da6 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -35,6 +35,7 @@ #include "machine-image.h" #include "macro.h" #include "mkdir.h" +#include "os-util.h" #include "path-util.h" #include "rm-rf.h" #include "string-table.h" @@ -923,6 +924,7 @@ int image_read_metadata(Image *i) { sd_id128_t machine_id = SD_ID128_NULL; _cleanup_free_ char *hostname = NULL; _cleanup_free_ char *path = NULL; + _cleanup_fclose_ FILE *f = NULL; r = chase_symlinks("/etc/hostname", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path); if (r < 0 && r != -ENOENT) @@ -962,18 +964,9 @@ int image_read_metadata(Image *i) { log_debug_errno(r, "Failed to parse machine-info data of %s: %m", i->name); } - path = mfree(path); - - r = chase_symlinks("/etc/os-release", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path); - if (r == -ENOENT) - r = chase_symlinks("/usr/lib/os-release", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path); - if (r < 0 && r != -ENOENT) - log_debug_errno(r, "Failed to chase os-release in image: %m"); - else if (r >= 0) { - r = load_env_file_pairs(NULL, path, NULL, &os_release); - if (r < 0) - log_debug_errno(r, "Failed to parse os-release data of %s: %m", i->name); - } + r = load_os_release_pairs(i->path, &os_release); + if (r < 0) + log_debug_errno(r, "Failed to read os-release in image, ignoring: %m"); free_and_replace(i->hostname, hostname); i->machine_id = machine_id; diff --git a/src/test/meson.build b/src/test/meson.build index 4a9982240b..619f0cd823 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -242,6 +242,10 @@ tests += [ [], []], + [['src/test/test-os-util.c'], + [], + []], + [['src/test/test-escape.c'], [], []], diff --git a/src/test/test-os-util.c b/src/test/test-os-util.c new file mode 100644 index 0000000000..8d8b52d7f6 --- /dev/null +++ b/src/test/test-os-util.c @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "log.h" +#include "os-util.h" + +static void test_path_is_os_tree(void) { + assert_se(path_is_os_tree("/") > 0); + assert_se(path_is_os_tree("/etc") == 0); + assert_se(path_is_os_tree("/idontexist") == -ENOENT); +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + test_path_is_os_tree(); + + return 0; +} diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index 6c9c132612..1697b2d777 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -52,12 +52,6 @@ static void test_is_symlink(void) { unlink(name_link); } -static void test_path_is_os_tree(void) { - assert_se(path_is_os_tree("/") > 0); - assert_se(path_is_os_tree("/etc") == 0); - assert_se(path_is_os_tree("/idontexist") == -ENOENT); -} - static void test_path_is_fs_type(void) { /* run might not be a mount point in build chroots */ if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0) { @@ -81,7 +75,6 @@ static void test_path_is_temporary_fs(void) { int main(int argc, char *argv[]) { test_files_same(); test_is_symlink(); - test_path_is_os_tree(); test_path_is_fs_type(); test_path_is_temporary_fs();