summaryrefslogtreecommitdiffstats
path: root/src/shared/os-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/os-util.c')
-rw-r--r--src/shared/os-util.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/shared/os-util.c b/src/shared/os-util.c
new file mode 100644
index 0000000..3b7e495
--- /dev/null
+++ b/src/shared/os-util.c
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "env-file.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "macro.h"
+#include "os-util.h"
+#include "string-util.h"
+#include "strv.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 distinguish 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 r, fd;
+
+ FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") {
+ r = chase_symlinks(p, root, CHASE_PREFIX_ROOT,
+ ret_path ? &q : NULL,
+ ret_fd ? &fd : NULL);
+ if (r != -ENOENT)
+ break;
+ }
+ if (r < 0)
+ return r;
+
+ if (ret_fd) {
+ int real_fd;
+
+ /* Convert the O_PATH fd into a proper, readable one */
+ real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ safe_close(fd);
+ 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 = take_fdopen(&fd, "r");
+ if (!f)
+ return -errno;
+
+ *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, 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, ret);
+}
+
+int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret) {
+ _cleanup_strv_free_ char **os_release_pairs = NULL, **os_release_pairs_prefixed = NULL;
+ char **p, **q;
+ int r;
+
+ r = load_os_release_pairs(root, &os_release_pairs);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH_PAIR(p, q, os_release_pairs) {
+ char *line;
+
+ /* We strictly return only the four main ID fields and ignore the rest */
+ if (!STR_IN_SET(*p, "ID", "VERSION_ID", "BUILD_ID", "VARIANT_ID"))
+ continue;
+
+ ascii_strlower(*p);
+ line = strjoin(prefix, *p, "=", *q);
+ if (!line)
+ return -ENOMEM;
+ r = strv_consume(&os_release_pairs_prefixed, line);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(os_release_pairs_prefixed);
+
+ return 0;
+}