File: | build-scan/../src/portable/portable.c |
Warning: | line 482, column 21 Potential leak of memory pointed to by 'name' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
2 | ||||
3 | #include <stdio_ext.h> | |||
4 | ||||
5 | #include "bus-common-errors.h" | |||
6 | #include "bus-error.h" | |||
7 | #include "conf-files.h" | |||
8 | #include "copy.h" | |||
9 | #include "def.h" | |||
10 | #include "dirent-util.h" | |||
11 | #include "dissect-image.h" | |||
12 | #include "fd-util.h" | |||
13 | #include "fileio.h" | |||
14 | #include "fs-util.h" | |||
15 | #include "io-util.h" | |||
16 | #include "locale-util.h" | |||
17 | #include "loop-util.h" | |||
18 | #include "machine-image.h" | |||
19 | #include "mkdir.h" | |||
20 | #include "os-util.h" | |||
21 | #include "path-lookup.h" | |||
22 | #include "portable.h" | |||
23 | #include "process-util.h" | |||
24 | #include "set.h" | |||
25 | #include "signal-util.h" | |||
26 | #include "socket-util.h" | |||
27 | #include "string-table.h" | |||
28 | #include "strv.h" | |||
29 | #include "user-util.h" | |||
30 | ||||
31 | static const char profile_dirs[] = CONF_PATHS_NULSTR("systemd/portable/profile")"/etc/" "systemd/portable/profile" "\0" "/run/" "systemd/portable/profile" "\0" "/usr/local/lib/" "systemd/portable/profile" "\0" "/usr/lib/" "systemd/portable/profile" "\0"; | |||
32 | ||||
33 | /* Markers used in the first line of our 20-portable.conf unit file drop-in to determine, that a) the unit file was | |||
34 | * dropped there by the portable service logic and b) for which image it was dropped there. */ | |||
35 | #define PORTABLE_DROPIN_MARKER_BEGIN"# Drop-in created for image '" "# Drop-in created for image '" | |||
36 | #define PORTABLE_DROPIN_MARKER_END"', do not edit." "', do not edit." | |||
37 | ||||
38 | static bool_Bool prefix_match(const char *unit, const char *prefix) { | |||
39 | const char *p; | |||
40 | ||||
41 | p = startswith(unit, prefix); | |||
42 | if (!p) | |||
43 | return false0; | |||
44 | ||||
45 | /* Only respect prefixes followed by dash or dot or when there's a complete match */ | |||
46 | return IN_SET(*p, '-', '.', '@', 0)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){'-', '.', '@', 0})/sizeof(int)]; switch( *p) { case '-': case '.': case '@': case 0: _found = 1; break ; default: break; } _found; }); | |||
47 | } | |||
48 | ||||
49 | static bool_Bool unit_match(const char *unit, char **matches) { | |||
50 | const char *dot; | |||
51 | char **i; | |||
52 | ||||
53 | dot = strrchr(unit, '.'); | |||
54 | if (!dot) | |||
55 | return false0; | |||
56 | ||||
57 | if (!STR_IN_SET(dot, ".service", ".socket", ".target", ".timer", ".path")(!!strv_find((((char**) ((const char*[]) { ".service", ".socket" , ".target", ".timer", ".path", ((void*)0) }))), (dot)))) | |||
58 | return false0; | |||
59 | ||||
60 | /* Empty match expression means: everything */ | |||
61 | if (strv_isempty(matches)) | |||
62 | return true1; | |||
63 | ||||
64 | /* Otherwise, at least one needs to match */ | |||
65 | STRV_FOREACH(i, matches)for ((i) = (matches); (i) && *(i); (i)++) | |||
66 | if (prefix_match(unit, *i)) | |||
67 | return true1; | |||
68 | ||||
69 | return false0; | |||
70 | } | |||
71 | ||||
72 | static PortableMetadata *portable_metadata_new(const char *name, int fd) { | |||
73 | PortableMetadata *m; | |||
74 | ||||
75 | m = malloc0(offsetof(PortableMetadata, name) + strlen(name) + 1)(calloc(1, (__builtin_offsetof(PortableMetadata, name) + strlen (name) + 1))); | |||
76 | if (!m) | |||
77 | return NULL((void*)0); | |||
78 | ||||
79 | strcpy(m->name, name); | |||
80 | m->fd = fd; | |||
81 | ||||
82 | return m; | |||
83 | } | |||
84 | ||||
85 | PortableMetadata *portable_metadata_unref(PortableMetadata *i) { | |||
86 | if (!i) | |||
87 | return NULL((void*)0); | |||
88 | ||||
89 | safe_close(i->fd); | |||
90 | free(i->source); | |||
91 | ||||
92 | return mfree(i); | |||
93 | } | |||
94 | ||||
95 | Hashmap *portable_metadata_hashmap_unref(Hashmap *h) { | |||
96 | ||||
97 | for (;;) { | |||
98 | PortableMetadata *i; | |||
99 | ||||
100 | i = hashmap_steal_first(h); | |||
101 | if (!i) | |||
102 | break; | |||
103 | ||||
104 | portable_metadata_unref(i); | |||
105 | } | |||
106 | ||||
107 | return hashmap_free(h); | |||
108 | } | |||
109 | ||||
110 | static int compare_metadata(PortableMetadata *const *x, PortableMetadata *const *y) { | |||
111 | return strcmp((*x)->name, (*y)->name); | |||
112 | } | |||
113 | ||||
114 | int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret) { | |||
115 | ||||
116 | _cleanup_free___attribute__((cleanup(freep))) PortableMetadata **sorted = NULL((void*)0); | |||
117 | Iterator iterator; | |||
118 | PortableMetadata *item; | |||
119 | size_t k = 0; | |||
120 | ||||
121 | sorted = new(PortableMetadata*, hashmap_size(unit_files))((PortableMetadata**) malloc_multiply(sizeof(PortableMetadata *), (hashmap_size(unit_files)))); | |||
122 | if (!sorted) | |||
123 | return -ENOMEM12; | |||
124 | ||||
125 | HASHMAP_FOREACH(item, unit_files, iterator)for ((iterator) = ((Iterator) { .idx = ((2147483647 *2U +1U) - 1), .next_key = ((void*)0) }); hashmap_iterate((unit_files), &(iterator), (void**)&(item), ((void*)0)); ) | |||
126 | sorted[k++] = item; | |||
127 | ||||
128 | assert(k == hashmap_size(unit_files))do { if ((__builtin_expect(!!(!(k == hashmap_size(unit_files) )),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("k == hashmap_size(unit_files)" ), "../src/portable/portable.c", 128, __PRETTY_FUNCTION__); } while (0); | |||
129 | ||||
130 | typesafe_qsort(sorted, k, compare_metadata)({ int (*_func_)(const typeof(sorted[0])*, const typeof(sorted [0])*) = compare_metadata; qsort_safe((sorted), (k), sizeof(( sorted)[0]), (__compar_fn_t) _func_); }); | |||
131 | ||||
132 | *ret = TAKE_PTR(sorted)({ typeof(sorted) _ptr_ = (sorted); (sorted) = ((void*)0); _ptr_ ; }); | |||
133 | return 0; | |||
134 | } | |||
135 | ||||
136 | static int send_item( | |||
137 | int socket_fd, | |||
138 | const char *name, | |||
139 | int fd) { | |||
140 | ||||
141 | union { | |||
142 | struct cmsghdr cmsghdr; | |||
143 | uint8_t buf[CMSG_SPACE(sizeof(int))((((sizeof(int)) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)) + (((sizeof (struct cmsghdr)) + sizeof (size_t ) - 1) & (size_t) ~(sizeof (size_t) - 1)))]; | |||
144 | } control = {}; | |||
145 | struct iovec iovec; | |||
146 | struct msghdr mh = { | |||
147 | .msg_control = &control, | |||
148 | .msg_controllen = sizeof(control), | |||
149 | .msg_iov = &iovec, | |||
150 | .msg_iovlen = 1, | |||
151 | }; | |||
152 | struct cmsghdr *cmsg; | |||
153 | _cleanup_close___attribute__((cleanup(closep))) int data_fd = -1; | |||
154 | ||||
155 | assert(socket_fd >= 0)do { if ((__builtin_expect(!!(!(socket_fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("socket_fd >= 0"), "../src/portable/portable.c" , 155, __PRETTY_FUNCTION__); } while (0); | |||
156 | assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name"), "../src/portable/portable.c", 156 , __PRETTY_FUNCTION__); } while (0); | |||
157 | assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/portable/portable.c" , 157, __PRETTY_FUNCTION__); } while (0); | |||
158 | ||||
159 | data_fd = fd_duplicate_data_fd(fd); | |||
160 | if (data_fd < 0) | |||
161 | return data_fd; | |||
162 | ||||
163 | cmsg = CMSG_FIRSTHDR(&mh)((size_t) (&mh)->msg_controllen >= sizeof (struct cmsghdr ) ? (struct cmsghdr *) (&mh)->msg_control : (struct cmsghdr *) 0); | |||
164 | cmsg->cmsg_level = SOL_SOCKET1; | |||
165 | cmsg->cmsg_type = SCM_RIGHTSSCM_RIGHTS; | |||
166 | cmsg->cmsg_len = CMSG_LEN(sizeof(int))((((sizeof (struct cmsghdr)) + sizeof (size_t) - 1) & (size_t ) ~(sizeof (size_t) - 1)) + (sizeof(int))); | |||
167 | memcpy(CMSG_DATA(cmsg)((cmsg)->__cmsg_data), &data_fd, sizeof(int)); | |||
168 | ||||
169 | mh.msg_controllen = CMSG_SPACE(sizeof(int))((((sizeof(int)) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)) + (((sizeof (struct cmsghdr)) + sizeof (size_t ) - 1) & (size_t) ~(sizeof (size_t) - 1))); | |||
170 | iovec = IOVEC_MAKE_STRING(name)(struct iovec) { .iov_base = ((char*) name), .iov_len = (strlen (name)) }; | |||
171 | ||||
172 | if (sendmsg(socket_fd, &mh, MSG_NOSIGNALMSG_NOSIGNAL) < 0) | |||
173 | return -errno(*__errno_location ()); | |||
174 | ||||
175 | return 0; | |||
176 | } | |||
177 | ||||
178 | static int recv_item( | |||
179 | int socket_fd, | |||
180 | char **ret_name, | |||
181 | int *ret_fd) { | |||
182 | ||||
183 | union { | |||
184 | struct cmsghdr cmsghdr; | |||
185 | uint8_t buf[CMSG_SPACE(sizeof(int))((((sizeof(int)) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)) + (((sizeof (struct cmsghdr)) + sizeof (size_t ) - 1) & (size_t) ~(sizeof (size_t) - 1)))]; | |||
186 | } control = {}; | |||
187 | char buffer[PATH_MAX4096+2]; | |||
188 | struct iovec iov = IOVEC_INIT(buffer, sizeof(buffer)-1){ .iov_base = (buffer), .iov_len = (sizeof(buffer)-1) }; | |||
189 | struct msghdr mh = { | |||
190 | .msg_control = &control, | |||
191 | .msg_controllen = sizeof(control), | |||
192 | .msg_iov = &iov, | |||
193 | .msg_iovlen = 1, | |||
194 | }; | |||
195 | struct cmsghdr *cmsg; | |||
196 | _cleanup_close___attribute__((cleanup(closep))) int found_fd = -1; | |||
197 | char *copy; | |||
198 | ssize_t n; | |||
199 | ||||
200 | assert(socket_fd >= 0)do { if ((__builtin_expect(!!(!(socket_fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("socket_fd >= 0"), "../src/portable/portable.c" , 200, __PRETTY_FUNCTION__); } while (0); | |||
201 | assert(ret_name)do { if ((__builtin_expect(!!(!(ret_name)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret_name"), "../src/portable/portable.c" , 201, __PRETTY_FUNCTION__); } while (0); | |||
202 | assert(ret_fd)do { if ((__builtin_expect(!!(!(ret_fd)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret_fd"), "../src/portable/portable.c", 202, __PRETTY_FUNCTION__); } while (0); | |||
203 | ||||
204 | n = recvmsg(socket_fd, &mh, MSG_CMSG_CLOEXECMSG_CMSG_CLOEXEC); | |||
205 | if (n < 0) | |||
206 | return -errno(*__errno_location ()); | |||
207 | ||||
208 | CMSG_FOREACH(cmsg, &mh)for ((cmsg) = ((size_t) (&mh)->msg_controllen >= sizeof (struct cmsghdr) ? (struct cmsghdr *) (&mh)->msg_control : (struct cmsghdr *) 0); (cmsg); (cmsg) = __cmsg_nxthdr ((& mh), (cmsg))) { | |||
209 | if (cmsg->cmsg_level == SOL_SOCKET1 && | |||
210 | cmsg->cmsg_type == SCM_RIGHTSSCM_RIGHTS) { | |||
211 | ||||
212 | if (cmsg->cmsg_len == CMSG_LEN(sizeof(int))((((sizeof (struct cmsghdr)) + sizeof (size_t) - 1) & (size_t ) ~(sizeof (size_t) - 1)) + (sizeof(int)))) { | |||
213 | assert(found_fd < 0)do { if ((__builtin_expect(!!(!(found_fd < 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("found_fd < 0"), "../src/portable/portable.c" , 213, __PRETTY_FUNCTION__); } while (0); | |||
214 | found_fd = *(int*) CMSG_DATA(cmsg)((cmsg)->__cmsg_data); | |||
215 | break; | |||
216 | } | |||
217 | ||||
218 | cmsg_close_all(&mh); | |||
219 | return -EIO5; | |||
220 | } | |||
221 | } | |||
222 | ||||
223 | buffer[n] = 0; | |||
224 | ||||
225 | copy = strdup(buffer); | |||
226 | if (!copy) | |||
227 | return -ENOMEM12; | |||
228 | ||||
229 | *ret_name = copy; | |||
230 | *ret_fd = TAKE_FD(found_fd)({ int _fd_ = (found_fd); (found_fd) = -1; _fd_; }); | |||
231 | ||||
232 | return 0; | |||
233 | } | |||
234 | ||||
235 | static int extract_now( | |||
236 | const char *where, | |||
237 | char **matches, | |||
238 | int socket_fd, | |||
239 | PortableMetadata **ret_os_release, | |||
240 | Hashmap **ret_unit_files) { | |||
241 | ||||
242 | _cleanup_(portable_metadata_hashmap_unrefp)__attribute__((cleanup(portable_metadata_hashmap_unrefp))) Hashmap *unit_files = NULL((void*)0); | |||
243 | _cleanup_(portable_metadata_unrefp)__attribute__((cleanup(portable_metadata_unrefp))) PortableMetadata *os_release = NULL((void*)0); | |||
244 | _cleanup_(lookup_paths_free)__attribute__((cleanup(lookup_paths_free))) LookupPaths paths = {}; | |||
245 | _cleanup_close___attribute__((cleanup(closep))) int os_release_fd = -1; | |||
246 | _cleanup_free___attribute__((cleanup(freep))) char *os_release_path = NULL((void*)0); | |||
247 | char **i; | |||
248 | int r; | |||
249 | ||||
250 | /* Extracts the metadata from a directory tree 'where'. Extracts two kinds of information: the /etc/os-release | |||
251 | * data, and all unit files matching the specified expression. Note that this function is called in two very | |||
252 | * different but also similar contexts. When the tool gets invoked on a directory tree, we'll process it | |||
253 | * directly, and in-process, and thus can return the requested data directly, via 'ret_os_release' and | |||
254 | * 'ret_unit_files'. However, if the tool is invoked on a raw disk image — which needs to be mounted first — we | |||
255 | * are invoked in a child process with private mounts and then need to send the collected data to our | |||
256 | * parent. To handle both cases in one call this function also gets a 'socket_fd' parameter, which when >= 0 is | |||
257 | * used to send the data to the parent. */ | |||
258 | ||||
259 | assert(where)do { if ((__builtin_expect(!!(!(where)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("where"), "../src/portable/portable.c", 259 , __PRETTY_FUNCTION__); } while (0); | |||
260 | ||||
261 | /* First, find /etc/os-release and send it upstream (or just save it). */ | |||
262 | r = open_os_release(where, &os_release_path, &os_release_fd); | |||
263 | if (r < 0) | |||
264 | log_debug_errno(r, "Couldn't acquire os-release file, ignoring: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 264, __func__, "Couldn't acquire os-release file, ignoring: %m" ) : -abs(_e); }); | |||
265 | else { | |||
266 | if (socket_fd >= 0) { | |||
267 | r = send_item(socket_fd, "/etc/os-release", os_release_fd); | |||
268 | if (r < 0) | |||
269 | return log_debug_errno(r, "Failed to send os-release file: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 269, __func__, "Failed to send os-release file: %m" ) : -abs(_e); }); | |||
270 | } | |||
271 | ||||
272 | if (ret_os_release) { | |||
273 | os_release = portable_metadata_new("/etc/os-release", os_release_fd); | |||
274 | if (!os_release) | |||
275 | return -ENOMEM12; | |||
276 | ||||
277 | os_release_fd = -1; | |||
278 | os_release->source = TAKE_PTR(os_release_path)({ typeof(os_release_path) _ptr_ = (os_release_path); (os_release_path ) = ((void*)0); _ptr_; }); | |||
279 | } | |||
280 | } | |||
281 | ||||
282 | /* Then, send unit file data to the parent (or/and add it to the hashmap). For that we use our usual unit | |||
283 | * discovery logic. Note that we force looking inside of /lib/systemd/system/ for units too, as we mightbe | |||
284 | * compiled for a split-usr system but the image might be a legacy-usr one. */ | |||
285 | r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, where); | |||
286 | if (r < 0) | |||
287 | return log_debug_errno(r, "Failed to acquire lookup paths: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 287, __func__, "Failed to acquire lookup paths: %m" ) : -abs(_e); }); | |||
288 | ||||
289 | unit_files = hashmap_new(&string_hash_ops)internal_hashmap_new(&string_hash_ops ); | |||
290 | if (!unit_files) | |||
291 | return -ENOMEM12; | |||
292 | ||||
293 | STRV_FOREACH(i, paths.search_path)for ((i) = (paths.search_path); (i) && *(i); (i)++) { | |||
294 | _cleanup_free___attribute__((cleanup(freep))) char *resolved = NULL((void*)0); | |||
295 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
296 | struct dirent *de; | |||
297 | ||||
298 | r = chase_symlinks_and_opendir(*i, where, 0, &resolved, &d); | |||
299 | if (r < 0) { | |||
300 | log_debug_errno(r, "Failed to open unit path '%s', ignoring: %m", *i)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 300, __func__, "Failed to open unit path '%s', ignoring: %m" , *i) : -abs(_e); }); | |||
301 | continue; | |||
302 | } | |||
303 | ||||
304 | FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to read directory: %m"))for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location ()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location ( )) > 0) { return ({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm (_realm) >= ((_level) & 0x07)) ? log_internal_realm((( _realm) << 10 | (_level)), _e, "../src/portable/portable.c" , 304, __func__, "Failed to read directory: %m") : -abs(_e); } ); } break; } else if (hidden_or_backup_file((de)->d_name) ) continue; else { | |||
305 | _cleanup_(portable_metadata_unrefp)__attribute__((cleanup(portable_metadata_unrefp))) PortableMetadata *m = NULL((void*)0); | |||
306 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
307 | ||||
308 | if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) | |||
309 | continue; | |||
310 | ||||
311 | if (!unit_match(de->d_name, matches)) | |||
312 | continue; | |||
313 | ||||
314 | /* Filter out duplicates */ | |||
315 | if (hashmap_get(unit_files, de->d_name)) | |||
316 | continue; | |||
317 | ||||
318 | dirent_ensure_type(d, de); | |||
319 | if (!IN_SET(de->d_type, DT_LNK, DT_REG)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){DT_LNK, DT_REG})/sizeof(int)]; switch(de ->d_type) { case DT_LNK: case DT_REG: _found = 1; break; default : break; } _found; })) | |||
320 | continue; | |||
321 | ||||
322 | fd = openat(dirfd(d), de->d_name, O_CLOEXEC02000000|O_RDONLY00); | |||
323 | if (fd < 0) { | |||
324 | log_debug_errno(errno, "Failed to open unit file '%s', ignoring: %m", de->d_name)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 324, __func__ , "Failed to open unit file '%s', ignoring: %m", de->d_name ) : -abs(_e); }); | |||
325 | continue; | |||
326 | } | |||
327 | ||||
328 | if (socket_fd >= 0) { | |||
329 | r = send_item(socket_fd, de->d_name, fd); | |||
330 | if (r < 0) | |||
331 | return log_debug_errno(r, "Failed to send unit metadata to parent: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 331, __func__, "Failed to send unit metadata to parent: %m" ) : -abs(_e); }); | |||
332 | } | |||
333 | ||||
334 | m = portable_metadata_new(de->d_name, fd); | |||
335 | if (!m) | |||
336 | return -ENOMEM12; | |||
337 | fd = -1; | |||
338 | ||||
339 | m->source = strjoin(resolved, "/", de->d_name)strjoin_real((resolved), "/", de->d_name, ((void*)0)); | |||
340 | if (!m->source) | |||
341 | return -ENOMEM12; | |||
342 | ||||
343 | r = hashmap_put(unit_files, m->name, m); | |||
344 | if (r < 0) | |||
345 | return log_debug_errno(r, "Failed to add unit to hashmap: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 345, __func__, "Failed to add unit to hashmap: %m" ) : -abs(_e); }); | |||
346 | m = NULL((void*)0); | |||
347 | } | |||
348 | } | |||
349 | ||||
350 | if (ret_os_release) | |||
351 | *ret_os_release = TAKE_PTR(os_release)({ typeof(os_release) _ptr_ = (os_release); (os_release) = (( void*)0); _ptr_; }); | |||
352 | if (ret_unit_files) | |||
353 | *ret_unit_files = TAKE_PTR(unit_files)({ typeof(unit_files) _ptr_ = (unit_files); (unit_files) = (( void*)0); _ptr_; }); | |||
354 | ||||
355 | return 0; | |||
356 | } | |||
357 | ||||
358 | static int portable_extract_by_path( | |||
359 | const char *path, | |||
360 | char **matches, | |||
361 | PortableMetadata **ret_os_release, | |||
362 | Hashmap **ret_unit_files, | |||
363 | sd_bus_error *error) { | |||
364 | ||||
365 | _cleanup_(portable_metadata_hashmap_unrefp)__attribute__((cleanup(portable_metadata_hashmap_unrefp))) Hashmap *unit_files = NULL((void*)0); | |||
366 | _cleanup_(portable_metadata_unrefp)__attribute__((cleanup(portable_metadata_unrefp))) PortableMetadata* os_release = NULL((void*)0); | |||
367 | _cleanup_(loop_device_unrefp)__attribute__((cleanup(loop_device_unrefp))) LoopDevice *d = NULL((void*)0); | |||
368 | int r; | |||
369 | ||||
370 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/portable/portable.c", 370 , __PRETTY_FUNCTION__); } while (0); | |||
| ||||
371 | ||||
372 | r = loop_device_make_by_path(path, O_RDONLY00, &d); | |||
373 | if (r == -EISDIR21) { | |||
374 | /* We can't turn this into a loop-back block device, and this returns EISDIR? Then this is a directory | |||
375 | * tree and not a raw device. It's easy then. */ | |||
376 | ||||
377 | r = extract_now(path, matches, -1, &os_release, &unit_files); | |||
378 | if (r < 0) | |||
379 | return r; | |||
380 | ||||
381 | } else if (r < 0) | |||
382 | return log_debug_errno(r, "Failed to set up loopback device: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 382, __func__, "Failed to set up loopback device: %m" ) : -abs(_e); }); | |||
383 | else { | |||
384 | _cleanup_(dissected_image_unrefp)__attribute__((cleanup(dissected_image_unrefp))) DissectedImage *m = NULL((void*)0); | |||
385 | _cleanup_(rmdir_and_freep)__attribute__((cleanup(rmdir_and_freep))) char *tmpdir = NULL((void*)0); | |||
386 | _cleanup_(close_pairp)__attribute__((cleanup(close_pairp))) int seq[2] = { -1, -1 }; | |||
387 | _cleanup_(sigkill_waitp)__attribute__((cleanup(sigkill_waitp))) pid_t child = 0; | |||
388 | ||||
389 | /* We now have a loopback block device, let's fork off a child in its own mount namespace, mount it | |||
390 | * there, and extract the metadata we need. The metadata is sent from the child back to us. */ | |||
391 | ||||
392 | BLOCK_SIGNALS(SIGCHLD)__attribute__((cleanup(block_signals_reset))) __attribute__ ( (unused)) sigset_t _saved_sigset = ({ sigset_t _t; do { if (( __builtin_expect(!!(!(sigprocmask_many(0, &_t, 17, -1) >= 0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("sigprocmask_many(SIG_BLOCK, &_t, 17, -1) >= 0" ), "../src/portable/portable.c", 392, __PRETTY_FUNCTION__); } while (0); _t; }); | |||
393 | ||||
394 | r = mkdtemp_malloc("/tmp/inspect-XXXXXX", &tmpdir); | |||
395 | if (r < 0) | |||
396 | return log_debug_errno(r, "Failed to create temporary directory: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 396, __func__, "Failed to create temporary directory: %m" ) : -abs(_e); }); | |||
397 | ||||
398 | r = dissect_image(d->fd, NULL((void*)0), 0, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP, &m); | |||
399 | if (r == -ENOPKG65) | |||
400 | sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS"org.freedesktop.DBus.Error.InvalidArgs", "Couldn't identify a suitable partition table or file system in '%s'.", path); | |||
401 | else if (r == -EADDRNOTAVAIL99) | |||
402 | sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS"org.freedesktop.DBus.Error.InvalidArgs", "No root partition for specified root hash found in '%s'.", path); | |||
403 | else if (r == -ENOTUNIQ76) | |||
404 | sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS"org.freedesktop.DBus.Error.InvalidArgs", "Multiple suitable root partitions found in image '%s'.", path); | |||
405 | else if (r == -ENXIO6) | |||
406 | sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS"org.freedesktop.DBus.Error.InvalidArgs", "No suitable root partition found in image '%s'.", path); | |||
407 | else if (r == -EPROTONOSUPPORT93) | |||
408 | sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS"org.freedesktop.DBus.Error.InvalidArgs", "Device '%s' is loopback block device with partition scanning turned off, please turn it on.", path); | |||
409 | if (r < 0) | |||
410 | return r; | |||
411 | ||||
412 | if (socketpair(AF_UNIX1, SOCK_SEQPACKETSOCK_SEQPACKET|SOCK_CLOEXECSOCK_CLOEXEC, 0, seq) < 0) | |||
413 | return log_debug_errno(errno, "Failed to allocated SOCK_SEQPACKET socket: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 413, __func__ , "Failed to allocated SOCK_SEQPACKET socket: %m") : -abs(_e) ; }); | |||
414 | ||||
415 | r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_LOG, &child); | |||
416 | if (r < 0) | |||
417 | return r; | |||
418 | if (r == 0) { | |||
419 | seq[0] = safe_close(seq[0]); | |||
420 | ||||
421 | r = dissected_image_mount(m, tmpdir, UID_INVALID((uid_t) -1), DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_VALIDATE_OS); | |||
422 | if (r < 0) { | |||
423 | log_debug_errno(r, "Failed to mount dissected image: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 423, __func__, "Failed to mount dissected image: %m" ) : -abs(_e); }); | |||
424 | goto child_finish; | |||
425 | } | |||
426 | ||||
427 | r = extract_now(tmpdir, matches, seq[1], NULL((void*)0), NULL((void*)0)); | |||
428 | ||||
429 | child_finish: | |||
430 | _exit(r < 0 ? EXIT_FAILURE1 : EXIT_SUCCESS0); | |||
431 | } | |||
432 | ||||
433 | seq[1] = safe_close(seq[1]); | |||
434 | ||||
435 | unit_files = hashmap_new(&string_hash_ops)internal_hashmap_new(&string_hash_ops ); | |||
436 | if (!unit_files) | |||
437 | return -ENOMEM12; | |||
438 | ||||
439 | for (;;) { | |||
440 | _cleanup_(portable_metadata_unrefp)__attribute__((cleanup(portable_metadata_unrefp))) PortableMetadata *add = NULL((void*)0); | |||
441 | _cleanup_free___attribute__((cleanup(freep))) char *name = NULL((void*)0); | |||
442 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
443 | ||||
444 | r = recv_item(seq[0], &name, &fd); | |||
445 | if (r
| |||
446 | return log_debug_errno(r, "Failed to receive item: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 446, __func__, "Failed to receive item: %m" ) : -abs(_e); }); | |||
447 | ||||
448 | /* We can't really distuingish a zero-length datagram without any fds from EOF (both are signalled the | |||
449 | * same way by recvmsg()). Hence, accept either as end notification. */ | |||
450 | if (isempty(name) && fd
| |||
451 | break; | |||
452 | ||||
453 | if (isempty(name) || fd < 0) { | |||
454 | log_debug("Invalid item sent from child.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 454, __func__, "Invalid item sent from child." ) : -abs(_e); }); | |||
455 | return -EINVAL22; | |||
456 | } | |||
457 | ||||
458 | add = portable_metadata_new(name, fd); | |||
459 | if (!add) | |||
460 | return -ENOMEM12; | |||
461 | fd = -1; | |||
462 | ||||
463 | /* Note that we do not initialize 'add->source' here, as the source path is not usable here as | |||
464 | * it refers to a path only valid in the short-living namespaced child process we forked | |||
465 | * here. */ | |||
466 | ||||
467 | if (PORTABLE_METADATA_IS_UNIT(add)(!({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, '/'})/sizeof(int)]; switch((add)-> name[0]) { case 0: case '/': _found = 1; break; default: break ; } _found; }))) { | |||
468 | r = hashmap_put(unit_files, add->name, add); | |||
469 | if (r < 0) | |||
470 | return log_debug_errno(r, "Failed to add item to unit file list: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 470, __func__, "Failed to add item to unit file list: %m" ) : -abs(_e); }); | |||
471 | ||||
472 | add = NULL((void*)0); | |||
473 | ||||
474 | } else if (PORTABLE_METADATA_IS_OS_RELEASE(add)((strcmp(((add)->name),("/etc/os-release")) == 0))) { | |||
475 | ||||
476 | assert(!os_release)do { if ((__builtin_expect(!!(!(!os_release)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("!os_release"), "../src/portable/portable.c" , 476, __PRETTY_FUNCTION__); } while (0); | |||
477 | os_release = TAKE_PTR(add)({ typeof(add) _ptr_ = (add); (add) = ((void*)0); _ptr_; }); | |||
478 | } else | |||
479 | assert_not_reached("Unexpected metadata item from child.")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, ( "Unexpected metadata item from child."), "../src/portable/portable.c" , 479, __PRETTY_FUNCTION__); } while (0); | |||
480 | } | |||
481 | ||||
482 | r = wait_for_terminate_and_check("(sd-dissect)", child, 0); | |||
| ||||
483 | if (r < 0) | |||
484 | return r; | |||
485 | child = 0; | |||
486 | } | |||
487 | ||||
488 | if (!os_release) | |||
489 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS"org.freedesktop.DBus.Error.InvalidArgs", "Image '%s' lacks os-release data, refusing.", path); | |||
490 | ||||
491 | if (hashmap_isempty(unit_files)) | |||
492 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS"org.freedesktop.DBus.Error.InvalidArgs", "Couldn't find any matching unit files in image '%s', refusing.", path); | |||
493 | ||||
494 | if (ret_unit_files) | |||
495 | *ret_unit_files = TAKE_PTR(unit_files)({ typeof(unit_files) _ptr_ = (unit_files); (unit_files) = (( void*)0); _ptr_; }); | |||
496 | ||||
497 | if (ret_os_release) | |||
498 | *ret_os_release = TAKE_PTR(os_release)({ typeof(os_release) _ptr_ = (os_release); (os_release) = (( void*)0); _ptr_; }); | |||
499 | ||||
500 | return 0; | |||
501 | } | |||
502 | ||||
503 | int portable_extract( | |||
504 | const char *name_or_path, | |||
505 | char **matches, | |||
506 | PortableMetadata **ret_os_release, | |||
507 | Hashmap **ret_unit_files, | |||
508 | sd_bus_error *error) { | |||
509 | ||||
510 | _cleanup_(image_unrefp)__attribute__((cleanup(image_unrefp))) Image *image = NULL((void*)0); | |||
511 | int r; | |||
512 | ||||
513 | assert(name_or_path)do { if ((__builtin_expect(!!(!(name_or_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name_or_path"), "../src/portable/portable.c" , 513, __PRETTY_FUNCTION__); } while (0); | |||
514 | ||||
515 | r = image_find_harder(IMAGE_PORTABLE, name_or_path, &image); | |||
516 | if (r < 0) | |||
517 | return r; | |||
518 | ||||
519 | return portable_extract_by_path(image->path, matches, ret_os_release, ret_unit_files, error); | |||
520 | } | |||
521 | ||||
522 | static int unit_file_is_active( | |||
523 | sd_bus *bus, | |||
524 | const char *name, | |||
525 | sd_bus_error *error) { | |||
526 | ||||
527 | static const char *const active_states[] = { | |||
528 | "activating", | |||
529 | "active", | |||
530 | "reloading", | |||
531 | "deactivating", | |||
532 | NULL((void*)0), | |||
533 | }; | |||
534 | int r; | |||
535 | ||||
536 | if (!bus) | |||
537 | return false0; | |||
538 | ||||
539 | /* If we are looking at a plain or instance things are easy, we can just query the state */ | |||
540 | if (unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) { | |||
541 | _cleanup_free___attribute__((cleanup(freep))) char *path = NULL((void*)0), *buf = NULL((void*)0); | |||
542 | ||||
543 | path = unit_dbus_path_from_name(name); | |||
544 | if (!path) | |||
545 | return -ENOMEM12; | |||
546 | ||||
547 | r = sd_bus_get_property_string( | |||
548 | bus, | |||
549 | "org.freedesktop.systemd1", | |||
550 | path, | |||
551 | "org.freedesktop.systemd1.Unit", | |||
552 | "ActiveState", | |||
553 | error, | |||
554 | &buf); | |||
555 | if (r < 0) | |||
556 | return log_debug_errno(r, "Failed to retrieve unit state: %s", bus_error_message(error, r))({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 556, __func__, "Failed to retrieve unit state: %s" , bus_error_message(error, r)) : -abs(_e); }); | |||
557 | ||||
558 | return strv_contains((char**) active_states, buf)(!!strv_find(((char**) active_states), (buf))); | |||
559 | } | |||
560 | ||||
561 | /* Otherwise we need to enumerate. But let's build the most restricted query we can */ | |||
562 | if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { | |||
563 | _cleanup_(sd_bus_message_unrefp)__attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *m = NULL((void*)0), *reply = NULL((void*)0); | |||
564 | const char *at, *prefix, *joined; | |||
565 | ||||
566 | r = sd_bus_message_new_method_call( | |||
567 | bus, | |||
568 | &m, | |||
569 | "org.freedesktop.systemd1", | |||
570 | "/org/freedesktop/systemd1", | |||
571 | "org.freedesktop.systemd1.Manager", | |||
572 | "ListUnitsByPatterns"); | |||
573 | if (r < 0) | |||
574 | return r; | |||
575 | ||||
576 | r = sd_bus_message_append_strv(m, (char**) active_states); | |||
577 | if (r < 0) | |||
578 | return r; | |||
579 | ||||
580 | at = strchr(name, '@'); | |||
581 | assert(at)do { if ((__builtin_expect(!!(!(at)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("at"), "../src/portable/portable.c", 581 , __PRETTY_FUNCTION__); } while (0); | |||
582 | ||||
583 | prefix = strndupa(name, at + 1 - name)(__extension__ ({ const char *__old = (name); size_t __len = strnlen (__old, (at + 1 - name)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old , __len); })); | |||
584 | joined = strjoina(prefix, "*", at + 1)({ const char *_appendees_[] = { prefix, "*", at + 1 }; char * _d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
585 | ||||
586 | r = sd_bus_message_append_strv(m, STRV_MAKE(joined)((char**) ((const char*[]) { joined, ((void*)0) }))); | |||
587 | if (r < 0) | |||
588 | return r; | |||
589 | ||||
590 | r = sd_bus_call(bus, m, 0, error, &reply); | |||
591 | if (r < 0) | |||
592 | return log_debug_errno(r, "Failed to list units: %s", bus_error_message(error, r))({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 592, __func__, "Failed to list units: %s" , bus_error_message(error, r)) : -abs(_e); }); | |||
593 | ||||
594 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); | |||
595 | if (r < 0) | |||
596 | return r; | |||
597 | ||||
598 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_STRUCT, "ssssssouso"); | |||
599 | if (r < 0) | |||
600 | return r; | |||
601 | ||||
602 | return r > 0; | |||
603 | } | |||
604 | ||||
605 | return -EINVAL22; | |||
606 | } | |||
607 | ||||
608 | static int portable_changes_add( | |||
609 | PortableChange **changes, | |||
610 | size_t *n_changes, | |||
611 | PortableChangeType type, | |||
612 | const char *path, | |||
613 | const char *source) { | |||
614 | ||||
615 | _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0), *s = NULL((void*)0); | |||
616 | PortableChange *c; | |||
617 | ||||
618 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/portable/portable.c", 618 , __PRETTY_FUNCTION__); } while (0); | |||
619 | assert(!changes == !n_changes)do { if ((__builtin_expect(!!(!(!changes == !n_changes)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!changes == !n_changes" ), "../src/portable/portable.c", 619, __PRETTY_FUNCTION__); } while (0); | |||
620 | ||||
621 | if (!changes) | |||
622 | return 0; | |||
623 | ||||
624 | c = reallocarray(*changes, *n_changes + 1, sizeof(PortableChange)); | |||
625 | if (!c) | |||
626 | return -ENOMEM12; | |||
627 | *changes = c; | |||
628 | ||||
629 | p = strdup(path); | |||
630 | if (!p) | |||
631 | return -ENOMEM12; | |||
632 | ||||
633 | path_simplify(p, false0); | |||
634 | ||||
635 | if (source) { | |||
636 | s = strdup(source); | |||
637 | if (!s) | |||
638 | return -ENOMEM12; | |||
639 | ||||
640 | path_simplify(s, false0); | |||
641 | } | |||
642 | ||||
643 | c[(*n_changes)++] = (PortableChange) { | |||
644 | .type = type, | |||
645 | .path = TAKE_PTR(p)({ typeof(p) _ptr_ = (p); (p) = ((void*)0); _ptr_; }), | |||
646 | .source = TAKE_PTR(s)({ typeof(s) _ptr_ = (s); (s) = ((void*)0); _ptr_; }), | |||
647 | }; | |||
648 | ||||
649 | return 0; | |||
650 | } | |||
651 | ||||
652 | static int portable_changes_add_with_prefix( | |||
653 | PortableChange **changes, | |||
654 | size_t *n_changes, | |||
655 | PortableChangeType type, | |||
656 | const char *prefix, | |||
657 | const char *path, | |||
658 | const char *source) { | |||
659 | ||||
660 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/portable/portable.c", 660 , __PRETTY_FUNCTION__); } while (0); | |||
661 | assert(!changes == !n_changes)do { if ((__builtin_expect(!!(!(!changes == !n_changes)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!changes == !n_changes" ), "../src/portable/portable.c", 661, __PRETTY_FUNCTION__); } while (0); | |||
662 | ||||
663 | if (!changes) | |||
664 | return 0; | |||
665 | ||||
666 | if (prefix) { | |||
667 | path = strjoina(prefix, "/", path)({ const char *_appendees_[] = { prefix, "/", path }; char *_d_ , *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
668 | ||||
669 | if (source) | |||
670 | source = strjoina(prefix, "/", source)({ const char *_appendees_[] = { prefix, "/", source }; char * _d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
671 | } | |||
672 | ||||
673 | return portable_changes_add(changes, n_changes, type, path, source); | |||
674 | } | |||
675 | ||||
676 | void portable_changes_free(PortableChange *changes, size_t n_changes) { | |||
677 | size_t i; | |||
678 | ||||
679 | assert(changes || n_changes == 0)do { if ((__builtin_expect(!!(!(changes || n_changes == 0)),0 ))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("changes || n_changes == 0" ), "../src/portable/portable.c", 679, __PRETTY_FUNCTION__); } while (0); | |||
680 | ||||
681 | for (i = 0; i < n_changes; i++) { | |||
682 | free(changes[i].path); | |||
683 | free(changes[i].source); | |||
684 | } | |||
685 | ||||
686 | free(changes); | |||
687 | } | |||
688 | ||||
689 | static int install_chroot_dropin( | |||
690 | const char *image_path, | |||
691 | ImageType type, | |||
692 | const PortableMetadata *m, | |||
693 | const char *dropin_dir, | |||
694 | char **ret_dropin, | |||
695 | PortableChange **changes, | |||
696 | size_t *n_changes) { | |||
697 | ||||
698 | _cleanup_free___attribute__((cleanup(freep))) char *text = NULL((void*)0), *dropin = NULL((void*)0); | |||
699 | int r; | |||
700 | ||||
701 | assert(image_path)do { if ((__builtin_expect(!!(!(image_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("image_path"), "../src/portable/portable.c" , 701, __PRETTY_FUNCTION__); } while (0); | |||
702 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/portable/portable.c", 702, __PRETTY_FUNCTION__); } while (0); | |||
703 | assert(dropin_dir)do { if ((__builtin_expect(!!(!(dropin_dir)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dropin_dir"), "../src/portable/portable.c" , 703, __PRETTY_FUNCTION__); } while (0); | |||
704 | ||||
705 | dropin = strjoin(dropin_dir, "/20-portable.conf")strjoin_real((dropin_dir), "/20-portable.conf", ((void*)0)); | |||
706 | if (!dropin) | |||
707 | return -ENOMEM12; | |||
708 | ||||
709 | text = strjoin(PORTABLE_DROPIN_MARKER_BEGIN, image_path, PORTABLE_DROPIN_MARKER_END "\n")strjoin_real(("# Drop-in created for image '"), image_path, "', do not edit." "\n", ((void*)0)); | |||
710 | if (!text) | |||
711 | return -ENOMEM12; | |||
712 | ||||
713 | if (endswith(m->name, ".service")) | |||
714 | if (!strextend(&text,strextend_with_separator(&text, ((void*)0), "\n" "[Service]\n" , ({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){IMAGE_DIRECTORY, IMAGE_SUBVOLUME})/sizeof (int)]; switch(type) { case IMAGE_DIRECTORY: case IMAGE_SUBVOLUME : _found = 1; break; default: break; } _found; }) ? "RootDirectory=" : "RootImage=", image_path, "\n" "Environment=PORTABLE=", basename (image_path), "\n" "LogExtraFields=PORTABLE=", basename(image_path ), "\n", ((void*)0)) | |||
715 | "\n"strextend_with_separator(&text, ((void*)0), "\n" "[Service]\n" , ({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){IMAGE_DIRECTORY, IMAGE_SUBVOLUME})/sizeof (int)]; switch(type) { case IMAGE_DIRECTORY: case IMAGE_SUBVOLUME : _found = 1; break; default: break; } _found; }) ? "RootDirectory=" : "RootImage=", image_path, "\n" "Environment=PORTABLE=", basename (image_path), "\n" "LogExtraFields=PORTABLE=", basename(image_path ), "\n", ((void*)0)) | |||
716 | "[Service]\n",strextend_with_separator(&text, ((void*)0), "\n" "[Service]\n" , ({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){IMAGE_DIRECTORY, IMAGE_SUBVOLUME})/sizeof (int)]; switch(type) { case IMAGE_DIRECTORY: case IMAGE_SUBVOLUME : _found = 1; break; default: break; } _found; }) ? "RootDirectory=" : "RootImage=", image_path, "\n" "Environment=PORTABLE=", basename (image_path), "\n" "LogExtraFields=PORTABLE=", basename(image_path ), "\n", ((void*)0)) | |||
717 | IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "RootDirectory=" : "RootImage=", image_path, "\n"strextend_with_separator(&text, ((void*)0), "\n" "[Service]\n" , ({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){IMAGE_DIRECTORY, IMAGE_SUBVOLUME})/sizeof (int)]; switch(type) { case IMAGE_DIRECTORY: case IMAGE_SUBVOLUME : _found = 1; break; default: break; } _found; }) ? "RootDirectory=" : "RootImage=", image_path, "\n" "Environment=PORTABLE=", basename (image_path), "\n" "LogExtraFields=PORTABLE=", basename(image_path ), "\n", ((void*)0)) | |||
718 | "Environment=PORTABLE=", basename(image_path), "\n"strextend_with_separator(&text, ((void*)0), "\n" "[Service]\n" , ({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){IMAGE_DIRECTORY, IMAGE_SUBVOLUME})/sizeof (int)]; switch(type) { case IMAGE_DIRECTORY: case IMAGE_SUBVOLUME : _found = 1; break; default: break; } _found; }) ? "RootDirectory=" : "RootImage=", image_path, "\n" "Environment=PORTABLE=", basename (image_path), "\n" "LogExtraFields=PORTABLE=", basename(image_path ), "\n", ((void*)0)) | |||
719 | "LogExtraFields=PORTABLE=", basename(image_path), "\n",strextend_with_separator(&text, ((void*)0), "\n" "[Service]\n" , ({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){IMAGE_DIRECTORY, IMAGE_SUBVOLUME})/sizeof (int)]; switch(type) { case IMAGE_DIRECTORY: case IMAGE_SUBVOLUME : _found = 1; break; default: break; } _found; }) ? "RootDirectory=" : "RootImage=", image_path, "\n" "Environment=PORTABLE=", basename (image_path), "\n" "LogExtraFields=PORTABLE=", basename(image_path ), "\n", ((void*)0)) | |||
720 | NULL)strextend_with_separator(&text, ((void*)0), "\n" "[Service]\n" , ({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){IMAGE_DIRECTORY, IMAGE_SUBVOLUME})/sizeof (int)]; switch(type) { case IMAGE_DIRECTORY: case IMAGE_SUBVOLUME : _found = 1; break; default: break; } _found; }) ? "RootDirectory=" : "RootImage=", image_path, "\n" "Environment=PORTABLE=", basename (image_path), "\n" "LogExtraFields=PORTABLE=", basename(image_path ), "\n", ((void*)0))) | |||
721 | ||||
722 | return -ENOMEM12; | |||
723 | ||||
724 | r = write_string_file(dropin, text, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); | |||
725 | if (r < 0) | |||
726 | return log_debug_errno(r, "Failed to write '%s': %m", dropin)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 726, __func__, "Failed to write '%s': %m" , dropin) : -abs(_e); }); | |||
727 | ||||
728 | (void) portable_changes_add(changes, n_changes, PORTABLE_WRITE, dropin, NULL((void*)0)); | |||
729 | ||||
730 | if (ret_dropin) | |||
731 | *ret_dropin = TAKE_PTR(dropin)({ typeof(dropin) _ptr_ = (dropin); (dropin) = ((void*)0); _ptr_ ; }); | |||
732 | ||||
733 | return 0; | |||
734 | } | |||
735 | ||||
736 | static int find_profile(const char *name, const char *unit, char **ret) { | |||
737 | const char *p, *dot; | |||
738 | ||||
739 | assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name"), "../src/portable/portable.c", 739 , __PRETTY_FUNCTION__); } while (0); | |||
740 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/portable/portable.c", 740 , __PRETTY_FUNCTION__); } while (0); | |||
741 | ||||
742 | assert_se(dot = strrchr(unit, '.'))do { if ((__builtin_expect(!!(!(dot = strrchr(unit, '.'))),0) )) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("dot = strrchr(unit, '.')" ), "../src/portable/portable.c", 742, __PRETTY_FUNCTION__); } while (0); | |||
743 | ||||
744 | NULSTR_FOREACH(p, profile_dirs)for ((p) = (profile_dirs); (p) && *(p); (p) = strchr( (p), 0)+1) { | |||
745 | _cleanup_free___attribute__((cleanup(freep))) char *joined; | |||
746 | ||||
747 | joined = strjoin(p, "/", name, "/", dot + 1, ".conf")strjoin_real((p), "/", name, "/", dot + 1, ".conf", ((void*)0 )); | |||
748 | if (!joined) | |||
749 | return -ENOMEM12; | |||
750 | ||||
751 | if (laccess(joined, F_OK)faccessat(-100, (joined), (0), 0x100) >= 0) { | |||
752 | *ret = TAKE_PTR(joined)({ typeof(joined) _ptr_ = (joined); (joined) = ((void*)0); _ptr_ ; }); | |||
753 | return 0; | |||
754 | } | |||
755 | ||||
756 | if (errno(*__errno_location ()) != ENOENT2) | |||
757 | return -errno(*__errno_location ()); | |||
758 | } | |||
759 | ||||
760 | return -ENOENT2; | |||
761 | } | |||
762 | ||||
763 | static int install_profile_dropin( | |||
764 | const char *image_path, | |||
765 | const PortableMetadata *m, | |||
766 | const char *dropin_dir, | |||
767 | const char *profile, | |||
768 | PortableFlags flags, | |||
769 | char **ret_dropin, | |||
770 | PortableChange **changes, | |||
771 | size_t *n_changes) { | |||
772 | ||||
773 | _cleanup_free___attribute__((cleanup(freep))) char *dropin = NULL((void*)0), *from = NULL((void*)0); | |||
774 | int r; | |||
775 | ||||
776 | assert(image_path)do { if ((__builtin_expect(!!(!(image_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("image_path"), "../src/portable/portable.c" , 776, __PRETTY_FUNCTION__); } while (0); | |||
777 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/portable/portable.c", 777, __PRETTY_FUNCTION__); } while (0); | |||
778 | assert(dropin_dir)do { if ((__builtin_expect(!!(!(dropin_dir)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dropin_dir"), "../src/portable/portable.c" , 778, __PRETTY_FUNCTION__); } while (0); | |||
779 | ||||
780 | if (!profile) | |||
781 | return 0; | |||
782 | ||||
783 | r = find_profile(profile, m->name, &from); | |||
784 | if (r < 0) { | |||
785 | if (r != ENOENT2) | |||
786 | return log_debug_errno(errno, "Profile '%s' is not accessible: %m", profile)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 786, __func__ , "Profile '%s' is not accessible: %m", profile) : -abs(_e); } ); | |||
787 | ||||
788 | log_debug_errno(errno, "Skipping link to profile '%s', as it does not exist: %m", profile)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 788, __func__ , "Skipping link to profile '%s', as it does not exist: %m", profile ) : -abs(_e); }); | |||
789 | return 0; | |||
790 | } | |||
791 | ||||
792 | dropin = strjoin(dropin_dir, "/10-profile.conf")strjoin_real((dropin_dir), "/10-profile.conf", ((void*)0)); | |||
793 | if (!dropin) | |||
794 | return -ENOMEM12; | |||
795 | ||||
796 | if (flags & PORTABLE_PREFER_COPY) { | |||
797 | ||||
798 | r = copy_file_atomic(from, dropin, 0644, 0, COPY_REFLINK); | |||
799 | if (r < 0) | |||
800 | return log_debug_errno(r, "Failed to copy %s %s %s: %m", from, special_glyph(ARROW), dropin)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 800, __func__, "Failed to copy %s %s %s: %m" , from, special_glyph(ARROW), dropin) : -abs(_e); }); | |||
801 | ||||
802 | (void) portable_changes_add(changes, n_changes, PORTABLE_COPY, dropin, from); | |||
803 | ||||
804 | } else { | |||
805 | ||||
806 | if (symlink(from, dropin) < 0) | |||
807 | return log_debug_errno(errno, "Failed to link %s %s %s: %m", from, special_glyph(ARROW), dropin)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 807, __func__ , "Failed to link %s %s %s: %m", from, special_glyph(ARROW), dropin ) : -abs(_e); }); | |||
808 | ||||
809 | (void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, dropin, from); | |||
810 | } | |||
811 | ||||
812 | if (ret_dropin) | |||
813 | *ret_dropin = TAKE_PTR(dropin)({ typeof(dropin) _ptr_ = (dropin); (dropin) = ((void*)0); _ptr_ ; }); | |||
814 | ||||
815 | return 0; | |||
816 | } | |||
817 | ||||
818 | static const char *config_path(const LookupPaths *paths, PortableFlags flags) { | |||
819 | const char *where; | |||
820 | ||||
821 | assert(paths)do { if ((__builtin_expect(!!(!(paths)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("paths"), "../src/portable/portable.c", 821 , __PRETTY_FUNCTION__); } while (0); | |||
822 | ||||
823 | if (flags & PORTABLE_RUNTIME) | |||
824 | where = paths->runtime_config; | |||
825 | else | |||
826 | where = paths->persistent_config; | |||
827 | ||||
828 | assert(where)do { if ((__builtin_expect(!!(!(where)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("where"), "../src/portable/portable.c", 828 , __PRETTY_FUNCTION__); } while (0); | |||
829 | return where; | |||
830 | } | |||
831 | ||||
832 | static int attach_unit_file( | |||
833 | const LookupPaths *paths, | |||
834 | const char *image_path, | |||
835 | ImageType type, | |||
836 | const PortableMetadata *m, | |||
837 | const char *profile, | |||
838 | PortableFlags flags, | |||
839 | PortableChange **changes, | |||
840 | size_t *n_changes) { | |||
841 | ||||
842 | _cleanup_(unlink_and_freep)__attribute__((cleanup(unlink_and_freep))) char *chroot_dropin = NULL((void*)0), *profile_dropin = NULL((void*)0); | |||
843 | _cleanup_(rmdir_and_freep)__attribute__((cleanup(rmdir_and_freep))) char *dropin_dir = NULL((void*)0); | |||
844 | const char *where, *path; | |||
845 | int r; | |||
846 | ||||
847 | assert(paths)do { if ((__builtin_expect(!!(!(paths)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("paths"), "../src/portable/portable.c", 847 , __PRETTY_FUNCTION__); } while (0); | |||
848 | assert(image_path)do { if ((__builtin_expect(!!(!(image_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("image_path"), "../src/portable/portable.c" , 848, __PRETTY_FUNCTION__); } while (0); | |||
849 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/portable/portable.c", 849, __PRETTY_FUNCTION__); } while (0); | |||
850 | assert(PORTABLE_METADATA_IS_UNIT(m))do { if ((__builtin_expect(!!(!((!({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, '/'})/sizeof(int)]; switch((m)->name [0]) { case 0: case '/': _found = 1; break; default: break; } _found; })))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("PORTABLE_METADATA_IS_UNIT(m)"), "../src/portable/portable.c" , 850, __PRETTY_FUNCTION__); } while (0); | |||
851 | ||||
852 | where = config_path(paths, flags); | |||
853 | path = strjoina(where, "/", m->name)({ const char *_appendees_[] = { where, "/", m->name }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
854 | ||||
855 | dropin_dir = strjoin(path, ".d")strjoin_real((path), ".d", ((void*)0)); | |||
856 | if (!dropin_dir) | |||
857 | return -ENOMEM12; | |||
858 | ||||
859 | (void) mkdir_p(dropin_dir, 0755); | |||
860 | (void) portable_changes_add(changes, n_changes, PORTABLE_MKDIR, dropin_dir, NULL((void*)0)); | |||
861 | ||||
862 | /* We install the drop-ins first, and the actual unit file last to achieve somewhat atomic behaviour if PID 1 | |||
863 | * is reloaded while we are creating things here: as long as only the drop-ins exist the unit doesn't exist at | |||
864 | * all for PID 1. */ | |||
865 | ||||
866 | r = install_chroot_dropin(image_path, type, m, dropin_dir, &chroot_dropin, changes, n_changes); | |||
867 | if (r < 0) | |||
868 | return r; | |||
869 | ||||
870 | r = install_profile_dropin(image_path, m, dropin_dir, profile, flags, &profile_dropin, changes, n_changes); | |||
871 | if (r < 0) | |||
872 | return r; | |||
873 | ||||
874 | if ((flags & PORTABLE_PREFER_SYMLINK) && m->source) { | |||
875 | ||||
876 | if (symlink(m->source, path) < 0) | |||
877 | return log_debug_errno(errno, "Failed to symlink unit file '%s': %m", path)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 877, __func__ , "Failed to symlink unit file '%s': %m", path) : -abs(_e); } ); | |||
878 | ||||
879 | (void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, path, m->source); | |||
880 | ||||
881 | } else { | |||
882 | _cleanup_(unlink_and_freep)__attribute__((cleanup(unlink_and_freep))) char *tmp = NULL((void*)0); | |||
883 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
884 | ||||
885 | fd = open_tmpfile_linkable(where, O_WRONLY01|O_CLOEXEC02000000, &tmp); | |||
886 | if (fd < 0) | |||
887 | return log_debug_errno(fd, "Failed to create unit file '%s': %m", path)({ int _level = ((7)), _e = ((fd)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 887, __func__, "Failed to create unit file '%s': %m" , path) : -abs(_e); }); | |||
888 | ||||
889 | r = copy_bytes(m->fd, fd, UINT64_MAX(18446744073709551615UL), COPY_REFLINK); | |||
890 | if (r < 0) | |||
891 | return log_debug_errno(r, "Failed to copy unit file '%s': %m", path)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 891, __func__, "Failed to copy unit file '%s': %m" , path) : -abs(_e); }); | |||
892 | ||||
893 | if (fchmod(fd, 0644) < 0) | |||
894 | return log_debug_errno(errno, "Failed to change unit file access mode for '%s': %m", path)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 894, __func__ , "Failed to change unit file access mode for '%s': %m", path ) : -abs(_e); }); | |||
895 | ||||
896 | r = link_tmpfile(fd, tmp, path); | |||
897 | if (r < 0) | |||
898 | return log_debug_errno(r, "Failed to install unit file '%s': %m", path)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 898, __func__, "Failed to install unit file '%s': %m" , path) : -abs(_e); }); | |||
899 | ||||
900 | tmp = mfree(tmp); | |||
901 | ||||
902 | (void) portable_changes_add(changes, n_changes, PORTABLE_COPY, path, m->source); | |||
903 | } | |||
904 | ||||
905 | /* All is established now, now let's disable any rollbacks */ | |||
906 | chroot_dropin = mfree(chroot_dropin); | |||
907 | profile_dropin = mfree(profile_dropin); | |||
908 | dropin_dir = mfree(dropin_dir); | |||
909 | ||||
910 | return 0; | |||
911 | } | |||
912 | ||||
913 | static int image_symlink( | |||
914 | const char *image_path, | |||
915 | PortableFlags flags, | |||
916 | char **ret) { | |||
917 | ||||
918 | const char *fn, *where; | |||
919 | char *joined = NULL((void*)0); | |||
920 | ||||
921 | assert(image_path)do { if ((__builtin_expect(!!(!(image_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("image_path"), "../src/portable/portable.c" , 921, __PRETTY_FUNCTION__); } while (0); | |||
922 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/portable/portable.c", 922 , __PRETTY_FUNCTION__); } while (0); | |||
923 | ||||
924 | fn = last_path_component(image_path); | |||
925 | ||||
926 | if (flags & PORTABLE_RUNTIME) | |||
927 | where = "/run/portables/"; | |||
928 | else | |||
929 | where = "/etc/portables/"; | |||
930 | ||||
931 | joined = strjoin(where, fn)strjoin_real((where), fn, ((void*)0)); | |||
932 | if (!joined) | |||
933 | return -ENOMEM12; | |||
934 | ||||
935 | *ret = joined; | |||
936 | return 0; | |||
937 | } | |||
938 | ||||
939 | static int install_image_symlink( | |||
940 | const char *image_path, | |||
941 | PortableFlags flags, | |||
942 | PortableChange **changes, | |||
943 | size_t *n_changes) { | |||
944 | ||||
945 | _cleanup_free___attribute__((cleanup(freep))) char *sl = NULL((void*)0); | |||
946 | int r; | |||
947 | ||||
948 | assert(image_path)do { if ((__builtin_expect(!!(!(image_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("image_path"), "../src/portable/portable.c" , 948, __PRETTY_FUNCTION__); } while (0); | |||
949 | ||||
950 | /* If the image is outside of the image search also link it into it, so that it can be found with short image | |||
951 | * names and is listed among the images. */ | |||
952 | ||||
953 | if (image_in_search_path(IMAGE_PORTABLE, image_path)) | |||
954 | return 0; | |||
955 | ||||
956 | r = image_symlink(image_path, flags, &sl); | |||
957 | if (r < 0) | |||
958 | return log_debug_errno(r, "Failed to generate image symlink path: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 958, __func__, "Failed to generate image symlink path: %m" ) : -abs(_e); }); | |||
959 | ||||
960 | (void) mkdir_parents(sl, 0755); | |||
961 | ||||
962 | if (symlink(image_path, sl) < 0) | |||
963 | return log_debug_errno(errno, "Failed to link %s %s %s: %m", image_path, special_glyph(ARROW), sl)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 963, __func__ , "Failed to link %s %s %s: %m", image_path, special_glyph(ARROW ), sl) : -abs(_e); }); | |||
964 | ||||
965 | (void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, sl, image_path); | |||
966 | return 0; | |||
967 | } | |||
968 | ||||
969 | int portable_attach( | |||
970 | sd_bus *bus, | |||
971 | const char *name_or_path, | |||
972 | char **matches, | |||
973 | const char *profile, | |||
974 | PortableFlags flags, | |||
975 | PortableChange **changes, | |||
976 | size_t *n_changes, | |||
977 | sd_bus_error *error) { | |||
978 | ||||
979 | _cleanup_(portable_metadata_hashmap_unrefp)__attribute__((cleanup(portable_metadata_hashmap_unrefp))) Hashmap *unit_files = NULL((void*)0); | |||
980 | _cleanup_(lookup_paths_free)__attribute__((cleanup(lookup_paths_free))) LookupPaths paths = {}; | |||
981 | _cleanup_(image_unrefp)__attribute__((cleanup(image_unrefp))) Image *image = NULL((void*)0); | |||
982 | PortableMetadata *item; | |||
983 | Iterator iterator; | |||
984 | int r; | |||
985 | ||||
986 | assert(name_or_path)do { if ((__builtin_expect(!!(!(name_or_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name_or_path"), "../src/portable/portable.c" , 986, __PRETTY_FUNCTION__); } while (0); | |||
987 | ||||
988 | r = image_find_harder(IMAGE_PORTABLE, name_or_path, &image); | |||
989 | if (r < 0) | |||
990 | return r; | |||
991 | ||||
992 | r = portable_extract_by_path(image->path, matches, NULL((void*)0), &unit_files, error); | |||
993 | if (r < 0) | |||
994 | return r; | |||
995 | ||||
996 | r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL((void*)0)); | |||
997 | if (r < 0) | |||
998 | return r; | |||
999 | ||||
1000 | HASHMAP_FOREACH(item, unit_files, iterator)for ((iterator) = ((Iterator) { .idx = ((2147483647 *2U +1U) - 1), .next_key = ((void*)0) }); hashmap_iterate((unit_files), &(iterator), (void**)&(item), ((void*)0)); ) { | |||
1001 | r = unit_file_exists(UNIT_FILE_SYSTEM, &paths, item->name); | |||
1002 | if (r < 0) | |||
1003 | return sd_bus_error_set_errnof(error, r, "Failed to determine whether unit '%s' exists on the host: %m", item->name); | |||
1004 | if (r > 0) | |||
1005 | return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS"org.freedesktop.systemd1.UnitExists", "Unit file '%s' exists on the host already, refusing.", item->name); | |||
1006 | ||||
1007 | r = unit_file_is_active(bus, item->name, error); | |||
1008 | if (r < 0) | |||
1009 | return r; | |||
1010 | if (r > 0) | |||
1011 | return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS"org.freedesktop.systemd1.UnitExists", "Unit file '%s' is active already, refusing.", item->name); | |||
1012 | } | |||
1013 | ||||
1014 | HASHMAP_FOREACH(item, unit_files, iterator)for ((iterator) = ((Iterator) { .idx = ((2147483647 *2U +1U) - 1), .next_key = ((void*)0) }); hashmap_iterate((unit_files), &(iterator), (void**)&(item), ((void*)0)); ) { | |||
1015 | r = attach_unit_file(&paths, image->path, image->type, item, profile, flags, changes, n_changes); | |||
1016 | if (r < 0) | |||
1017 | return r; | |||
1018 | } | |||
1019 | ||||
1020 | /* We don't care too much for the image symlink, it's just a convenience thing, it's not necessary for proper | |||
1021 | * operation otherwise. */ | |||
1022 | (void) install_image_symlink(image->path, flags, changes, n_changes); | |||
1023 | ||||
1024 | return 0; | |||
1025 | } | |||
1026 | ||||
1027 | static bool_Bool marker_matches_image(const char *marker, const char *name_or_path) { | |||
1028 | const char *a; | |||
1029 | ||||
1030 | assert(marker)do { if ((__builtin_expect(!!(!(marker)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("marker"), "../src/portable/portable.c", 1030, __PRETTY_FUNCTION__); } while (0); | |||
1031 | assert(name_or_path)do { if ((__builtin_expect(!!(!(name_or_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name_or_path"), "../src/portable/portable.c" , 1031, __PRETTY_FUNCTION__); } while (0); | |||
1032 | ||||
1033 | a = last_path_component(marker); | |||
1034 | ||||
1035 | if (image_name_is_valid(name_or_path)) { | |||
1036 | const char *e; | |||
1037 | ||||
1038 | /* We shall match against an image name. In that case let's compare the last component, and optionally | |||
1039 | * allow either a suffix of ".raw" or a series of "/". */ | |||
1040 | ||||
1041 | e = startswith(a, name_or_path); | |||
1042 | if (!e) | |||
1043 | return false0; | |||
1044 | ||||
1045 | return | |||
1046 | e[strspn(e, "/")] == 0 || | |||
1047 | streq(e, ".raw")(strcmp((e),(".raw")) == 0); | |||
1048 | } else { | |||
1049 | const char *b; | |||
1050 | size_t l; | |||
1051 | ||||
1052 | /* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to | |||
1053 | * reach the same file. However, in this mode, let's validate any file suffix. */ | |||
1054 | ||||
1055 | l = strcspn(a, "/"); | |||
1056 | b = last_path_component(name_or_path); | |||
1057 | ||||
1058 | if (strcspn(b, "/") != l) | |||
1059 | return false0; | |||
1060 | ||||
1061 | return memcmp(a, b, l) == 0; | |||
1062 | } | |||
1063 | } | |||
1064 | ||||
1065 | static int test_chroot_dropin( | |||
1066 | DIR *d, | |||
1067 | const char *where, | |||
1068 | const char *fname, | |||
1069 | const char *name_or_path, | |||
1070 | char **ret_marker) { | |||
1071 | ||||
1072 | _cleanup_free___attribute__((cleanup(freep))) char *line = NULL((void*)0), *marker = NULL((void*)0); | |||
1073 | _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0); | |||
1074 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
1075 | const char *p, *e, *k; | |||
1076 | int r; | |||
1077 | ||||
1078 | assert(d)do { if ((__builtin_expect(!!(!(d)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("d"), "../src/portable/portable.c", 1078 , __PRETTY_FUNCTION__); } while (0); | |||
1079 | assert(where)do { if ((__builtin_expect(!!(!(where)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("where"), "../src/portable/portable.c", 1079 , __PRETTY_FUNCTION__); } while (0); | |||
1080 | assert(fname)do { if ((__builtin_expect(!!(!(fname)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fname"), "../src/portable/portable.c", 1080 , __PRETTY_FUNCTION__); } while (0); | |||
1081 | ||||
1082 | /* We recognize unis created from portable images via the drop-in we created for them */ | |||
1083 | ||||
1084 | p = strjoina(fname, ".d/20-portable.conf")({ const char *_appendees_[] = { fname, ".d/20-portable.conf" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
1085 | fd = openat(dirfd(d), p, O_RDONLY00|O_CLOEXEC02000000); | |||
1086 | if (fd < 0) { | |||
1087 | if (errno(*__errno_location ()) == ENOENT2) | |||
1088 | return 0; | |||
1089 | ||||
1090 | return log_debug_errno(errno, "Failed to open %s/%s: %m", where, p)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 1090, __func__ , "Failed to open %s/%s: %m", where, p) : -abs(_e); }); | |||
1091 | } | |||
1092 | ||||
1093 | f = fdopen(fd, "re"); | |||
1094 | if (!f) | |||
1095 | return log_debug_errno(errno, "Failed to convert file handle: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 1095, __func__ , "Failed to convert file handle: %m") : -abs(_e); }); | |||
1096 | fd = -1; | |||
1097 | ||||
1098 | (void) __fsetlocking(f, FSETLOCKING_BYCALLERFSETLOCKING_BYCALLER); | |||
1099 | ||||
1100 | r = read_line(f, LONG_LINE_MAX(1U*1024U*1024U), &line); | |||
1101 | if (r < 0) | |||
1102 | return log_debug_errno(r, "Failed to read from %s/%s: %m", where, p)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 1102, __func__, "Failed to read from %s/%s: %m" , where, p) : -abs(_e); }); | |||
1103 | ||||
1104 | e = startswith(line, PORTABLE_DROPIN_MARKER_BEGIN"# Drop-in created for image '"); | |||
1105 | if (!e) | |||
1106 | return 0; | |||
1107 | ||||
1108 | k = endswith(e, PORTABLE_DROPIN_MARKER_END"', do not edit."); | |||
1109 | if (!k) | |||
1110 | return 0; | |||
1111 | ||||
1112 | marker = strndup(e, k - e); | |||
1113 | if (!marker) | |||
1114 | return -ENOMEM12; | |||
1115 | ||||
1116 | if (!name_or_path) | |||
1117 | r = true1; | |||
1118 | else | |||
1119 | r = marker_matches_image(marker, name_or_path); | |||
1120 | ||||
1121 | if (ret_marker) | |||
1122 | *ret_marker = TAKE_PTR(marker)({ typeof(marker) _ptr_ = (marker); (marker) = ((void*)0); _ptr_ ; }); | |||
1123 | ||||
1124 | return r; | |||
1125 | } | |||
1126 | ||||
1127 | int portable_detach( | |||
1128 | sd_bus *bus, | |||
1129 | const char *name_or_path, | |||
1130 | PortableFlags flags, | |||
1131 | PortableChange **changes, | |||
1132 | size_t *n_changes, | |||
1133 | sd_bus_error *error) { | |||
1134 | ||||
1135 | _cleanup_(lookup_paths_free)__attribute__((cleanup(lookup_paths_free))) LookupPaths paths = {}; | |||
1136 | _cleanup_set_free_free___attribute__((cleanup(set_free_freep))) Set *unit_files = NULL((void*)0), *markers = NULL((void*)0); | |||
1137 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
1138 | const char *where, *item; | |||
1139 | Iterator iterator; | |||
1140 | struct dirent *de; | |||
1141 | int ret = 0; | |||
1142 | int r; | |||
1143 | ||||
1144 | assert(name_or_path)do { if ((__builtin_expect(!!(!(name_or_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name_or_path"), "../src/portable/portable.c" , 1144, __PRETTY_FUNCTION__); } while (0); | |||
1145 | ||||
1146 | r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL((void*)0)); | |||
1147 | if (r < 0) | |||
1148 | return r; | |||
1149 | ||||
1150 | where = config_path(&paths, flags); | |||
1151 | ||||
1152 | d = opendir(where); | |||
1153 | if (!d) | |||
1154 | return log_debug_errno(errno, "Failed to open '%s' directory: %m", where)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 1154, __func__ , "Failed to open '%s' directory: %m", where) : -abs(_e); }); | |||
1155 | ||||
1156 | unit_files = set_new(&string_hash_ops)internal_set_new(&string_hash_ops ); | |||
1157 | if (!unit_files) | |||
1158 | return -ENOMEM12; | |||
1159 | ||||
1160 | markers = set_new(&path_hash_ops)internal_set_new(&path_hash_ops ); | |||
1161 | if (!markers) | |||
1162 | return -ENOMEM12; | |||
1163 | ||||
1164 | FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to enumerate '%s' directory: %m", where))for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location ()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location ( )) > 0) { return ({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm (_realm) >= ((_level) & 0x07)) ? log_internal_realm((( _realm) << 10 | (_level)), _e, "../src/portable/portable.c" , 1164, __func__, "Failed to enumerate '%s' directory: %m", where ) : -abs(_e); }); } break; } else if (hidden_or_backup_file(( de)->d_name)) continue; else { | |||
1165 | _cleanup_free___attribute__((cleanup(freep))) char *marker = NULL((void*)0); | |||
1166 | UnitFileState state; | |||
1167 | ||||
1168 | if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) | |||
1169 | continue; | |||
1170 | ||||
1171 | /* Filter out duplicates */ | |||
1172 | if (set_get(unit_files, de->d_name)) | |||
1173 | continue; | |||
1174 | ||||
1175 | dirent_ensure_type(d, de); | |||
1176 | if (!IN_SET(de->d_type, DT_LNK, DT_REG)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){DT_LNK, DT_REG})/sizeof(int)]; switch(de ->d_type) { case DT_LNK: case DT_REG: _found = 1; break; default : break; } _found; })) | |||
1177 | continue; | |||
1178 | ||||
1179 | r = test_chroot_dropin(d, where, de->d_name, name_or_path, &marker); | |||
1180 | if (r < 0) | |||
1181 | return r; | |||
1182 | if (r == 0) | |||
1183 | continue; | |||
1184 | ||||
1185 | r = unit_file_lookup_state(UNIT_FILE_SYSTEM, &paths, de->d_name, &state); | |||
1186 | if (r < 0) | |||
1187 | return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 1187, __func__, "Failed to determine unit file state of '%s': %m" , de->d_name) : -abs(_e); }); | |||
1188 | if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_RUNTIME)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED , UNIT_FILE_RUNTIME})/sizeof(int)]; switch(state) { case UNIT_FILE_STATIC : case UNIT_FILE_DISABLED: case UNIT_FILE_LINKED: case UNIT_FILE_RUNTIME : _found = 1; break; default: break; } _found; })) | |||
1189 | return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS"org.freedesktop.systemd1.UnitExists", "Unit file '%s' is in state '%s', can't detach.", de->d_name, unit_file_state_to_string(state)); | |||
1190 | ||||
1191 | r = unit_file_is_active(bus, de->d_name, error); | |||
1192 | if (r < 0) | |||
1193 | return r; | |||
1194 | if (r > 0) | |||
1195 | return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS"org.freedesktop.systemd1.UnitExists", "Unit file '%s' is active, can't detach.", de->d_name); | |||
1196 | ||||
1197 | r = set_put_strdup(unit_files, de->d_name); | |||
1198 | if (r < 0) | |||
1199 | return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 1199, __func__, "Failed to add unit name '%s' to set: %m" , de->d_name) : -abs(_e); }); | |||
1200 | ||||
1201 | if (path_is_absolute(marker) && | |||
1202 | !image_in_search_path(IMAGE_PORTABLE, marker)) { | |||
1203 | ||||
1204 | r = set_ensure_allocated(&markers, &path_hash_ops)internal_set_ensure_allocated(&markers, &path_hash_ops ); | |||
1205 | if (r < 0) | |||
1206 | return r; | |||
1207 | ||||
1208 | r = set_put(markers, marker); | |||
1209 | if (r >= 0) | |||
1210 | marker = NULL((void*)0); | |||
1211 | else if (r != -EEXIST17) | |||
1212 | return r; | |||
1213 | } | |||
1214 | } | |||
1215 | ||||
1216 | if (set_isempty(unit_files)) { | |||
1217 | log_debug("No unit files associated with '%s' found. Image not attached?", name_or_path)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 1217, __func__, "No unit files associated with '%s' found. Image not attached?" , name_or_path) : -abs(_e); }); | |||
1218 | return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT"org.freedesktop.systemd1.NoSuchUnit", "No unit files associated with '%s' found. Image not attached?", name_or_path); | |||
1219 | } | |||
1220 | ||||
1221 | SET_FOREACH(item, unit_files, iterator)for ((iterator) = ((Iterator) { .idx = ((2147483647 *2U +1U) - 1), .next_key = ((void*)0) }); set_iterate((unit_files), & (iterator), (void**)&(item)); ) { | |||
1222 | _cleanup_free___attribute__((cleanup(freep))) char *md = NULL((void*)0); | |||
1223 | const char *suffix; | |||
1224 | ||||
1225 | if (unlinkat(dirfd(d), item, 0) < 0) { | |||
1226 | log_debug_errno(errno, "Can't remove unit file %s/%s: %m", where, item)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 1226, __func__ , "Can't remove unit file %s/%s: %m", where, item) : -abs(_e) ; }); | |||
1227 | ||||
1228 | if (errno(*__errno_location ()) != ENOENT2 && ret >= 0) | |||
1229 | ret = -errno(*__errno_location ()); | |||
1230 | } else | |||
1231 | portable_changes_add_with_prefix(changes, n_changes, PORTABLE_UNLINK, where, item, NULL((void*)0)); | |||
1232 | ||||
1233 | FOREACH_STRING(suffix, ".d/10-profile.conf", ".d/20-portable.conf")for (char **_l = ({ char **_ll = ((char**) ((const char*[]) { ".d/10-profile.conf", ".d/20-portable.conf", ((void*)0) })); suffix = _ll ? _ll[0] : ((void*)0); _ll; }); _l && * _l; suffix = ({ _l ++; _l[0]; })) { | |||
1234 | _cleanup_free___attribute__((cleanup(freep))) char *dropin = NULL((void*)0); | |||
1235 | ||||
1236 | dropin = strjoin(item, suffix)strjoin_real((item), suffix, ((void*)0)); | |||
1237 | if (!dropin) | |||
1238 | return -ENOMEM12; | |||
1239 | ||||
1240 | if (unlinkat(dirfd(d), dropin, 0) < 0) { | |||
1241 | log_debug_errno(errno, "Can't remove drop-in %s/%s: %m", where, dropin)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 1241, __func__ , "Can't remove drop-in %s/%s: %m", where, dropin) : -abs(_e) ; }); | |||
1242 | ||||
1243 | if (errno(*__errno_location ()) != ENOENT2 && ret >= 0) | |||
1244 | ret = -errno(*__errno_location ()); | |||
1245 | } else | |||
1246 | portable_changes_add_with_prefix(changes, n_changes, PORTABLE_UNLINK, where, dropin, NULL((void*)0)); | |||
1247 | } | |||
1248 | ||||
1249 | md = strjoin(item, ".d")strjoin_real((item), ".d", ((void*)0)); | |||
1250 | if (!md) | |||
1251 | return -ENOMEM12; | |||
1252 | ||||
1253 | if (unlinkat(dirfd(d), md, AT_REMOVEDIR0x200) < 0) { | |||
1254 | log_debug_errno(errno, "Can't remove drop-in directory %s/%s: %m", where, md)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 1254, __func__ , "Can't remove drop-in directory %s/%s: %m", where, md) : -abs (_e); }); | |||
1255 | ||||
1256 | if (errno(*__errno_location ()) != ENOENT2 && ret >= 0) | |||
1257 | ret = -errno(*__errno_location ()); | |||
1258 | } else | |||
1259 | portable_changes_add_with_prefix(changes, n_changes, PORTABLE_UNLINK, where, md, NULL((void*)0)); | |||
1260 | } | |||
1261 | ||||
1262 | /* Now, also drop any image symlink, for images outside of the sarch path */ | |||
1263 | SET_FOREACH(item, markers, iterator)for ((iterator) = ((Iterator) { .idx = ((2147483647 *2U +1U) - 1), .next_key = ((void*)0) }); set_iterate((markers), &( iterator), (void**)&(item)); ) { | |||
1264 | _cleanup_free___attribute__((cleanup(freep))) char *sl = NULL((void*)0); | |||
1265 | struct stat st; | |||
1266 | ||||
1267 | r = image_symlink(item, flags, &sl); | |||
1268 | if (r < 0) { | |||
1269 | log_debug_errno(r, "Failed to determine image symlink for '%s', ignoring: %m", item)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 1269, __func__, "Failed to determine image symlink for '%s', ignoring: %m" , item) : -abs(_e); }); | |||
1270 | continue; | |||
1271 | } | |||
1272 | ||||
1273 | if (lstat(sl, &st) < 0) { | |||
1274 | log_debug_errno(errno, "Failed to stat '%s', ignoring: %m", sl)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 1274, __func__ , "Failed to stat '%s', ignoring: %m", sl) : -abs(_e); }); | |||
1275 | continue; | |||
1276 | } | |||
1277 | ||||
1278 | if (!S_ISLNK(st.st_mode)((((st.st_mode)) & 0170000) == (0120000))) { | |||
1279 | log_debug("Image '%s' is not a symlink, ignoring.", sl)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 1279, __func__, "Image '%s' is not a symlink, ignoring." , sl) : -abs(_e); }); | |||
1280 | continue; | |||
1281 | } | |||
1282 | ||||
1283 | if (unlink(sl) < 0) { | |||
1284 | log_debug_errno(errno, "Can't remove image symlink '%s': %m", sl)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 1284, __func__ , "Can't remove image symlink '%s': %m", sl) : -abs(_e); }); | |||
1285 | ||||
1286 | if (errno(*__errno_location ()) != ENOENT2 && ret >= 0) | |||
1287 | ret = -errno(*__errno_location ()); | |||
1288 | } else | |||
1289 | portable_changes_add(changes, n_changes, PORTABLE_UNLINK, sl, NULL((void*)0)); | |||
1290 | } | |||
1291 | ||||
1292 | return ret; | |||
1293 | } | |||
1294 | ||||
1295 | static int portable_get_state_internal( | |||
1296 | sd_bus *bus, | |||
1297 | const char *name_or_path, | |||
1298 | PortableFlags flags, | |||
1299 | PortableState *ret, | |||
1300 | sd_bus_error *error) { | |||
1301 | ||||
1302 | _cleanup_(lookup_paths_free)__attribute__((cleanup(lookup_paths_free))) LookupPaths paths = {}; | |||
1303 | bool_Bool found_enabled = false0, found_running = false0; | |||
1304 | _cleanup_set_free_free___attribute__((cleanup(set_free_freep))) Set *unit_files = NULL((void*)0); | |||
1305 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
1306 | const char *where; | |||
1307 | struct dirent *de; | |||
1308 | int r; | |||
1309 | ||||
1310 | assert(name_or_path)do { if ((__builtin_expect(!!(!(name_or_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name_or_path"), "../src/portable/portable.c" , 1310, __PRETTY_FUNCTION__); } while (0); | |||
1311 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/portable/portable.c", 1311 , __PRETTY_FUNCTION__); } while (0); | |||
1312 | ||||
1313 | r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL((void*)0)); | |||
1314 | if (r < 0) | |||
1315 | return r; | |||
1316 | ||||
1317 | where = config_path(&paths, flags); | |||
1318 | ||||
1319 | d = opendir(where); | |||
1320 | if (!d) | |||
1321 | return log_debug_errno(errno, "Failed to open '%s' directory: %m", where)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/portable/portable.c", 1321, __func__ , "Failed to open '%s' directory: %m", where) : -abs(_e); }); | |||
1322 | ||||
1323 | unit_files = set_new(&string_hash_ops)internal_set_new(&string_hash_ops ); | |||
1324 | if (!unit_files) | |||
1325 | return -ENOMEM12; | |||
1326 | ||||
1327 | FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to enumerate '%s' directory: %m", where))for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location ()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location ( )) > 0) { return ({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm (_realm) >= ((_level) & 0x07)) ? log_internal_realm((( _realm) << 10 | (_level)), _e, "../src/portable/portable.c" , 1327, __func__, "Failed to enumerate '%s' directory: %m", where ) : -abs(_e); }); } break; } else if (hidden_or_backup_file(( de)->d_name)) continue; else { | |||
1328 | UnitFileState state; | |||
1329 | ||||
1330 | if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) | |||
1331 | continue; | |||
1332 | ||||
1333 | /* Filter out duplicates */ | |||
1334 | if (set_get(unit_files, de->d_name)) | |||
1335 | continue; | |||
1336 | ||||
1337 | dirent_ensure_type(d, de); | |||
1338 | if (!IN_SET(de->d_type, DT_LNK, DT_REG)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){DT_LNK, DT_REG})/sizeof(int)]; switch(de ->d_type) { case DT_LNK: case DT_REG: _found = 1; break; default : break; } _found; })) | |||
1339 | continue; | |||
1340 | ||||
1341 | r = test_chroot_dropin(d, where, de->d_name, name_or_path, NULL((void*)0)); | |||
1342 | if (r < 0) | |||
1343 | return r; | |||
1344 | if (r == 0) | |||
1345 | continue; | |||
1346 | ||||
1347 | r = unit_file_lookup_state(UNIT_FILE_SYSTEM, &paths, de->d_name, &state); | |||
1348 | if (r < 0) | |||
1349 | return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 1349, __func__, "Failed to determine unit file state of '%s': %m" , de->d_name) : -abs(_e); }); | |||
1350 | if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_LINKED_RUNTIME)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED , UNIT_FILE_LINKED_RUNTIME})/sizeof(int)]; switch(state) { case UNIT_FILE_STATIC: case UNIT_FILE_DISABLED: case UNIT_FILE_LINKED : case UNIT_FILE_LINKED_RUNTIME: _found = 1; break; default: break ; } _found; })) | |||
1351 | found_enabled = true1; | |||
1352 | ||||
1353 | r = unit_file_is_active(bus, de->d_name, error); | |||
1354 | if (r < 0) | |||
1355 | return r; | |||
1356 | if (r > 0) | |||
1357 | found_running = true1; | |||
1358 | ||||
1359 | r = set_put_strdup(unit_files, de->d_name); | |||
1360 | if (r < 0) | |||
1361 | return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/portable/portable.c", 1361, __func__, "Failed to add unit name '%s' to set: %m" , de->d_name) : -abs(_e); }); | |||
1362 | } | |||
1363 | ||||
1364 | *ret = found_running ? (!set_isempty(unit_files) && (flags & PORTABLE_RUNTIME) ? PORTABLE_RUNNING_RUNTIME : PORTABLE_RUNNING) : | |||
1365 | found_enabled ? (flags & PORTABLE_RUNTIME ? PORTABLE_ENABLED_RUNTIME : PORTABLE_ENABLED) : | |||
1366 | !set_isempty(unit_files) ? (flags & PORTABLE_RUNTIME ? PORTABLE_ATTACHED_RUNTIME : PORTABLE_ATTACHED) : PORTABLE_DETACHED; | |||
1367 | ||||
1368 | return 0; | |||
1369 | } | |||
1370 | ||||
1371 | int portable_get_state( | |||
1372 | sd_bus *bus, | |||
1373 | const char *name_or_path, | |||
1374 | PortableFlags flags, | |||
1375 | PortableState *ret, | |||
1376 | sd_bus_error *error) { | |||
1377 | ||||
1378 | PortableState state; | |||
1379 | int r; | |||
1380 | ||||
1381 | assert(name_or_path)do { if ((__builtin_expect(!!(!(name_or_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name_or_path"), "../src/portable/portable.c" , 1381, __PRETTY_FUNCTION__); } while (0); | |||
1382 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/portable/portable.c", 1382 , __PRETTY_FUNCTION__); } while (0); | |||
1383 | ||||
1384 | /* We look for matching units twice: once in the regular directories, and once in the runtime directories — but | |||
1385 | * the latter only if we didn't find anything in the former. */ | |||
1386 | ||||
1387 | r = portable_get_state_internal(bus, name_or_path, flags & ~PORTABLE_RUNTIME, &state, error); | |||
1388 | if (r < 0) | |||
1389 | return r; | |||
1390 | ||||
1391 | if (state == PORTABLE_DETACHED) { | |||
1392 | r = portable_get_state_internal(bus, name_or_path, flags | PORTABLE_RUNTIME, &state, error); | |||
1393 | if (r < 0) | |||
1394 | return r; | |||
1395 | } | |||
1396 | ||||
1397 | *ret = state; | |||
1398 | return 0; | |||
1399 | } | |||
1400 | ||||
1401 | int portable_get_profiles(char ***ret) { | |||
1402 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/portable/portable.c", 1402 , __PRETTY_FUNCTION__); } while (0); | |||
1403 | ||||
1404 | return conf_files_list_nulstr(ret, NULL((void*)0), NULL((void*)0), CONF_FILES_DIRECTORY|CONF_FILES_BASENAME|CONF_FILES_FILTER_MASKED, profile_dirs); | |||
1405 | } | |||
1406 | ||||
1407 | static const char* const portable_change_type_table[_PORTABLE_CHANGE_TYPE_MAX] = { | |||
1408 | [PORTABLE_COPY] = "copy", | |||
1409 | [PORTABLE_MKDIR] = "mkdir", | |||
1410 | [PORTABLE_SYMLINK] = "symlink", | |||
1411 | [PORTABLE_UNLINK] = "unlink", | |||
1412 | [PORTABLE_WRITE] = "write", | |||
1413 | }; | |||
1414 | ||||
1415 | DEFINE_STRING_TABLE_LOOKUP(portable_change_type, PortableChangeType)const char *portable_change_type_to_string(PortableChangeType i) { if (i < 0 || i >= (PortableChangeType) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (portable_change_type_table), typeof(&*(portable_change_type_table ))), sizeof(portable_change_type_table)/sizeof((portable_change_type_table )[0]), ((void)0)))) return ((void*)0); return portable_change_type_table [i]; } PortableChangeType portable_change_type_from_string(const char *s) { return (PortableChangeType) string_table_lookup(portable_change_type_table , __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(portable_change_type_table), typeof(&*(portable_change_type_table ))), sizeof(portable_change_type_table)/sizeof((portable_change_type_table )[0]), ((void)0))), s); }; | |||
1416 | ||||
1417 | static const char* const portable_state_table[_PORTABLE_STATE_MAX] = { | |||
1418 | [PORTABLE_DETACHED] = "detached", | |||
1419 | [PORTABLE_ATTACHED] = "attached", | |||
1420 | [PORTABLE_ATTACHED_RUNTIME] = "attached-runtime", | |||
1421 | [PORTABLE_ENABLED] = "enabled", | |||
1422 | [PORTABLE_ENABLED_RUNTIME] = "enabled-runtime", | |||
1423 | [PORTABLE_RUNNING] = "running", | |||
1424 | [PORTABLE_RUNNING_RUNTIME] = "running-runtime", | |||
1425 | }; | |||
1426 | ||||
1427 | DEFINE_STRING_TABLE_LOOKUP(portable_state, PortableState)const char *portable_state_to_string(PortableState i) { if (i < 0 || i >= (PortableState) __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(portable_state_table), typeof(&*(portable_state_table))), sizeof(portable_state_table )/sizeof((portable_state_table)[0]), ((void)0)))) return ((void *)0); return portable_state_table[i]; } PortableState portable_state_from_string (const char *s) { return (PortableState) string_table_lookup( portable_state_table, __extension__ (__builtin_choose_expr( ! __builtin_types_compatible_p(typeof(portable_state_table), typeof (&*(portable_state_table))), sizeof(portable_state_table) /sizeof((portable_state_table)[0]), ((void)0))), s); }; |