]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/os-util.c
sysext: move extension_release_validate() out of os-util.c
[thirdparty/systemd.git] / src / basic / os-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "env-file.h"
5 #include "env-util.h"
6 #include "fd-util.h"
7 #include "fileio.h"
8 #include "fs-util.h"
9 #include "macro.h"
10 #include "os-util.h"
11 #include "path-util.h"
12 #include "string-util.h"
13 #include "strv.h"
14 #include "utf8.h"
15
16 bool image_name_is_valid(const char *s) {
17 if (!filename_is_valid(s))
18 return false;
19
20 if (string_has_cc(s, NULL))
21 return false;
22
23 if (!utf8_is_valid(s))
24 return false;
25
26 /* Temporary files for atomically creating new files */
27 if (startswith(s, ".#"))
28 return false;
29
30 return true;
31 }
32
33 int path_is_extension_tree(const char *path, const char *extension) {
34 int r;
35
36 assert(path);
37
38 /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
39 * always results in -ENOENT, and we can properly distinguish the case where the whole root doesn't exist from
40 * the case where just the os-release file is missing. */
41 if (laccess(path, F_OK) < 0)
42 return -errno;
43
44 /* We use /usr/lib/extension-release.d/extension-release.NAME as flag file if something is a system extension,
45 * and {/etc|/usr/lib}/os-release as flag file if something is an OS (in case extension == NULL) */
46 r = open_extension_release(path, extension, NULL, NULL);
47 if (r == -ENOENT) /* We got nothing */
48 return 0;
49 if (r < 0)
50 return r;
51
52 return 1;
53 }
54
55 int open_extension_release(const char *root, const char *extension, char **ret_path, int *ret_fd) {
56 _cleanup_free_ char *q = NULL;
57 int r, fd;
58
59 if (extension) {
60 const char *extension_full_path;
61
62 if (!image_name_is_valid(extension))
63 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
64 "The extension name %s is invalid.", extension);
65
66 extension_full_path = strjoina("/usr/lib/extension-release.d/extension-release.", extension);
67 r = chase_symlinks(extension_full_path, root, CHASE_PREFIX_ROOT,
68 ret_path ? &q : NULL,
69 ret_fd ? &fd : NULL);
70 } else {
71 const char *p;
72
73 FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") {
74 r = chase_symlinks(p, root, CHASE_PREFIX_ROOT,
75 ret_path ? &q : NULL,
76 ret_fd ? &fd : NULL);
77 if (r != -ENOENT)
78 break;
79 }
80 }
81 if (r < 0)
82 return r;
83
84 if (ret_fd) {
85 int real_fd;
86
87 /* Convert the O_PATH fd into a proper, readable one */
88 real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NOCTTY);
89 safe_close(fd);
90 if (real_fd < 0)
91 return real_fd;
92
93 *ret_fd = real_fd;
94 }
95
96 if (ret_path)
97 *ret_path = TAKE_PTR(q);
98
99 return 0;
100 }
101
102 int fopen_extension_release(const char *root, const char *extension, char **ret_path, FILE **ret_file) {
103 _cleanup_free_ char *p = NULL;
104 _cleanup_close_ int fd = -1;
105 FILE *f;
106 int r;
107
108 if (!ret_file)
109 return open_extension_release(root, extension, ret_path, NULL);
110
111 r = open_extension_release(root, extension, ret_path ? &p : NULL, &fd);
112 if (r < 0)
113 return r;
114
115 f = take_fdopen(&fd, "r");
116 if (!f)
117 return -errno;
118
119 *ret_file = f;
120
121 if (ret_path)
122 *ret_path = TAKE_PTR(p);
123
124 return 0;
125 }
126
127 static int parse_release_internal(const char *root, const char *extension, va_list ap) {
128 _cleanup_fclose_ FILE *f = NULL;
129 _cleanup_free_ char *p = NULL;
130 int r;
131
132 r = fopen_extension_release(root, extension, &p, &f);
133 if (r < 0)
134 return r;
135
136 return parse_env_filev(f, p, ap);
137 }
138
139 int parse_extension_release(const char *root, const char *extension, ...) {
140 va_list ap;
141 int r;
142
143 va_start(ap, extension);
144 r = parse_release_internal(root, extension, ap);
145 va_end(ap);
146
147 return r;
148 }
149
150 int parse_os_release(const char *root, ...) {
151 va_list ap;
152 int r;
153
154 va_start(ap, root);
155 r = parse_release_internal(root, NULL, ap);
156 va_end(ap);
157
158 return r;
159 }
160
161 int load_os_release_pairs(const char *root, char ***ret) {
162 _cleanup_fclose_ FILE *f = NULL;
163 _cleanup_free_ char *p = NULL;
164 int r;
165
166 r = fopen_os_release(root, &p, &f);
167 if (r < 0)
168 return r;
169
170 return load_env_file_pairs(f, p, ret);
171 }
172
173 int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret) {
174 _cleanup_strv_free_ char **os_release_pairs = NULL, **os_release_pairs_prefixed = NULL;
175 char **p, **q;
176 int r;
177
178 r = load_os_release_pairs(root, &os_release_pairs);
179 if (r < 0)
180 return r;
181
182 STRV_FOREACH_PAIR(p, q, os_release_pairs) {
183 char *line;
184
185 /* We strictly return only the four main ID fields and ignore the rest */
186 if (!STR_IN_SET(*p, "ID", "VERSION_ID", "BUILD_ID", "VARIANT_ID"))
187 continue;
188
189 ascii_strlower(*p);
190 line = strjoin(prefix, *p, "=", *q);
191 if (!line)
192 return -ENOMEM;
193 r = strv_consume(&os_release_pairs_prefixed, line);
194 if (r < 0)
195 return r;
196 }
197
198 *ret = TAKE_PTR(os_release_pairs_prefixed);
199
200 return 0;
201 }
202
203 int load_extension_release_pairs(const char *root, const char *extension, char ***ret) {
204 _cleanup_fclose_ FILE *f = NULL;
205 _cleanup_free_ char *p = NULL;
206 int r;
207
208 r = fopen_extension_release(root, extension, &p, &f);
209 if (r < 0)
210 return r;
211
212 return load_env_file_pairs(f, p, ret);
213 }