]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/shared/dropin.c
ci: use -p and -f when creating dirs/removing files in mkosi job btrfs setup
[thirdparty/systemd.git] / src / shared / dropin.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <stdio.h>
4
5#include "alloc-util.h"
6#include "chase.h"
7#include "conf-files.h"
8#include "dropin.h"
9#include "escape.h"
10#include "fileio.h"
11#include "log.h"
12#include "path-util.h"
13#include "set.h"
14#include "stdio-util.h"
15#include "string-util.h"
16#include "strv.h"
17#include "unit-def.h"
18#include "unit-name.h"
19
20int drop_in_file(
21 const char *dir,
22 const char *unit,
23 unsigned level,
24 const char *name,
25 char **ret_unit_dir,
26 char **ret_path) {
27
28 _cleanup_free_ char *n = NULL, *unit_dir = NULL;
29
30 assert(dir);
31 assert(unit);
32 assert(name);
33
34 n = xescape(name, "/.");
35 if (!n)
36 return -ENOMEM;
37 if (!filename_is_valid(n))
38 return -EINVAL;
39
40 if (ret_unit_dir || ret_path) {
41 unit_dir = path_join(dir, strjoina(unit, ".d"));
42 if (!unit_dir)
43 return -ENOMEM;
44 }
45
46 if (ret_path) {
47 char prefix[DECIMAL_STR_MAX(unsigned) + 1] = {};
48
49 if (level != UINT_MAX)
50 xsprintf(prefix, "%u-", level);
51
52 _cleanup_free_ char *path = strjoin(unit_dir, "/", prefix, n, ".conf");
53 if (!path)
54 return -ENOMEM;
55
56 *ret_path = TAKE_PTR(path);
57 }
58
59 if (ret_unit_dir)
60 *ret_unit_dir = TAKE_PTR(unit_dir);
61
62 return 0;
63}
64
65int write_drop_in(
66 const char *dir,
67 const char *unit,
68 unsigned level,
69 const char *name,
70 const char *data) {
71
72 _cleanup_free_ char *p = NULL;
73 int r;
74
75 assert(dir);
76 assert(unit);
77 assert(name);
78 assert(data);
79
80 r = drop_in_file(dir, unit, level, name, /* ret_unit_dir= */ NULL, &p);
81 if (r < 0)
82 return r;
83
84 return write_string_file(p, data, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755|WRITE_STRING_FILE_LABEL);
85}
86
87int write_drop_in_format(
88 const char *dir,
89 const char *unit,
90 unsigned level,
91 const char *name,
92 const char *format, ...) {
93
94 _cleanup_free_ char *content = NULL;
95 va_list ap;
96 int r;
97
98 assert(dir);
99 assert(unit);
100 assert(name);
101 assert(format);
102
103 va_start(ap, format);
104 r = vasprintf(&content, format, ap);
105 va_end(ap);
106
107 if (r < 0)
108 return -ENOMEM;
109
110 return write_drop_in(dir, unit, level, name, content);
111}
112
113static int unit_file_add_dir(
114 const char *original_root,
115 const char *path,
116 char ***dirs) {
117
118 _cleanup_free_ char *chased = NULL;
119 int r;
120
121 assert(path);
122
123 /* This adds [original_root]/path to dirs, if it exists. */
124
125 r = chase(path, original_root, 0, &chased, NULL);
126 if (r == -ENOENT) /* Ignore -ENOENT, after all most units won't have a drop-in dir. */
127 return 0;
128 if (r == -ENAMETOOLONG) {
129 /* Also, ignore -ENAMETOOLONG but log about it. After all, users are not even able to create the
130 * drop-in dir in such case. This mostly happens for device units with an overly long /sys path. */
131 log_debug_errno(r, "Path '%s' too long, couldn't canonicalize, ignoring.", path);
132 return 0;
133 }
134 if (r < 0)
135 return log_warning_errno(r, "Failed to canonicalize path '%s': %m", path);
136
137 if (strv_consume(dirs, TAKE_PTR(chased)) < 0)
138 return log_oom();
139
140 return 0;
141}
142
143static int unit_file_find_dirs(
144 const char *original_root,
145 Set *unit_path_cache,
146 const char *unit_path,
147 const char *name,
148 const char *suffix,
149 char ***dirs) {
150
151 _cleanup_free_ char *prefix = NULL, *instance = NULL, *built = NULL;
152 bool is_instance, chopped;
153 const char *dash;
154 UnitType type;
155 char *path;
156 size_t n;
157 int r;
158
159 assert(unit_path);
160 assert(name);
161 assert(suffix);
162
163 path = strjoina(unit_path, "/", name, suffix);
164 if (!unit_path_cache || set_get(unit_path_cache, path)) {
165 r = unit_file_add_dir(original_root, path, dirs);
166 if (r < 0)
167 return r;
168 }
169
170 is_instance = unit_name_is_valid(name, UNIT_NAME_INSTANCE);
171 if (is_instance) { /* Also try the template dir */
172 _cleanup_free_ char *template = NULL;
173
174 r = unit_name_template(name, &template);
175 if (r < 0)
176 return log_error_errno(r, "Failed to generate template from unit name: %m");
177
178 r = unit_file_find_dirs(original_root, unit_path_cache, unit_path, template, suffix, dirs);
179 if (r < 0)
180 return r;
181 }
182
183 /* Return early for top level drop-ins. */
184 if (unit_type_from_string(name) >= 0)
185 return 0;
186
187 /* Let's see if there's a "-" prefix for this unit name. If so, let's invoke ourselves for it. This will then
188 * recursively do the same for all our prefixes. i.e. this means given "foo-bar-waldo.service" we'll also
189 * search "foo-bar-.service" and "foo-.service".
190 *
191 * Note the order in which we do it: we traverse up adding drop-ins on each step. This means the more specific
192 * drop-ins may override the more generic drop-ins, which is the intended behaviour. */
193
194 r = unit_name_to_prefix(name, &prefix);
195 if (r < 0)
196 return log_error_errno(r, "Failed to derive unit name prefix from unit name: %m");
197
198 chopped = false;
199 for (;;) {
200 dash = strrchr(prefix, '-');
201 if (!dash) /* No dash? if so we are done */
202 return 0;
203
204 n = (size_t) (dash - prefix);
205 if (n == 0) /* Leading dash? If so, we are done */
206 return 0;
207
208 if (prefix[n+1] != 0 || chopped) {
209 prefix[n+1] = 0;
210 break;
211 }
212
213 /* Trailing dash? If so, chop it off and try again, but not more than once. */
214 prefix[n] = 0;
215 chopped = true;
216 }
217
218 if (!unit_prefix_is_valid(prefix))
219 return 0;
220
221 type = unit_name_to_type(name);
222 if (type < 0)
223 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
224 "Failed to derive unit type from unit name: %s",
225 name);
226
227 if (is_instance) {
228 r = unit_name_to_instance(name, &instance);
229 if (r < 0)
230 return log_error_errno(r, "Failed to derive unit name instance from unit name: %m");
231 }
232
233 r = unit_name_build_from_type(prefix, instance, type, &built);
234 if (r < 0)
235 return log_error_errno(r, "Failed to build prefix unit name: %m");
236
237 return unit_file_find_dirs(original_root, unit_path_cache, unit_path, built, suffix, dirs);
238}
239
240int unit_file_find_dropin_paths(
241 const char *original_root,
242 char **lookup_path,
243 Set *unit_path_cache,
244 const char *dir_suffix,
245 const char *file_suffix,
246 const char *name,
247 const Set *aliases,
248 char ***ret) {
249
250 _cleanup_strv_free_ char **dirs = NULL;
251 const char *n;
252 int r;
253
254 assert(ret);
255
256 if (name)
257 STRV_FOREACH(p, lookup_path)
258 (void) unit_file_find_dirs(original_root, unit_path_cache, *p, name, dir_suffix, &dirs);
259
260 SET_FOREACH(n, aliases)
261 STRV_FOREACH(p, lookup_path)
262 (void) unit_file_find_dirs(original_root, unit_path_cache, *p, n, dir_suffix, &dirs);
263
264 /* All the names in the unit are of the same type so just grab one. */
265 n = name ?: (const char*) set_first(aliases);
266 if (n) {
267 UnitType type = _UNIT_TYPE_INVALID;
268
269 type = unit_name_to_type(n);
270 if (type < 0)
271 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
272 "Failed to derive unit type from unit name: %s", n);
273
274 /* Special top level drop in for "<unit type>.<suffix>". Add this last as it's the most generic
275 * and should be able to be overridden by more specific drop-ins. */
276 STRV_FOREACH(p, lookup_path)
277 (void) unit_file_find_dirs(original_root,
278 unit_path_cache,
279 *p,
280 unit_type_to_string(type),
281 dir_suffix,
282 &dirs);
283 }
284
285 if (strv_isempty(dirs)) {
286 *ret = NULL;
287 return 0;
288 }
289
290 r = conf_files_list_strv(ret, file_suffix, NULL, 0, (const char**) dirs);
291 if (r < 0)
292 return log_warning_errno(r, "Failed to create the list of configuration files: %m");
293
294 return 1;
295}