/src/systemd/src/shared/dropin.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | | |
3 | | #include <errno.h> |
4 | | #include <stdarg.h> |
5 | | #include <stdio.h> |
6 | | #include <stdlib.h> |
7 | | |
8 | | #include "alloc-util.h" |
9 | | #include "conf-files.h" |
10 | | #include "dirent-util.h" |
11 | | #include "dropin.h" |
12 | | #include "escape.h" |
13 | | #include "fd-util.h" |
14 | | #include "fileio-label.h" |
15 | | #include "fs-util.h" |
16 | | #include "hashmap.h" |
17 | | #include "log.h" |
18 | | #include "macro.h" |
19 | | #include "mkdir.h" |
20 | | #include "path-util.h" |
21 | | #include "set.h" |
22 | | #include "string-util.h" |
23 | | #include "strv.h" |
24 | | #include "unit-name.h" |
25 | | |
26 | | int drop_in_file(const char *dir, const char *unit, unsigned level, |
27 | 0 | const char *name, char **_p, char **_q) { |
28 | 0 |
|
29 | 0 | char prefix[DECIMAL_STR_MAX(unsigned)]; |
30 | 0 | _cleanup_free_ char *b = NULL; |
31 | 0 | char *p, *q; |
32 | 0 |
|
33 | 0 | assert(unit); |
34 | 0 | assert(name); |
35 | 0 | assert(_p); |
36 | 0 | assert(_q); |
37 | 0 |
|
38 | 0 | sprintf(prefix, "%u", level); |
39 | 0 |
|
40 | 0 | b = xescape(name, "/."); |
41 | 0 | if (!b) |
42 | 0 | return -ENOMEM; |
43 | 0 | |
44 | 0 | if (!filename_is_valid(b)) |
45 | 0 | return -EINVAL; |
46 | 0 | |
47 | 0 | p = strjoin(dir, "/", unit, ".d"); |
48 | 0 | if (!p) |
49 | 0 | return -ENOMEM; |
50 | 0 | |
51 | 0 | q = strjoin(p, "/", prefix, "-", b, ".conf"); |
52 | 0 | if (!q) { |
53 | 0 | free(p); |
54 | 0 | return -ENOMEM; |
55 | 0 | } |
56 | 0 | |
57 | 0 | *_p = p; |
58 | 0 | *_q = q; |
59 | 0 | return 0; |
60 | 0 | } |
61 | | |
62 | | int write_drop_in(const char *dir, const char *unit, unsigned level, |
63 | 0 | const char *name, const char *data) { |
64 | 0 |
|
65 | 0 | _cleanup_free_ char *p = NULL, *q = NULL; |
66 | 0 | int r; |
67 | 0 |
|
68 | 0 | assert(dir); |
69 | 0 | assert(unit); |
70 | 0 | assert(name); |
71 | 0 | assert(data); |
72 | 0 |
|
73 | 0 | r = drop_in_file(dir, unit, level, name, &p, &q); |
74 | 0 | if (r < 0) |
75 | 0 | return r; |
76 | 0 | |
77 | 0 | (void) mkdir_p(p, 0755); |
78 | 0 | return write_string_file_atomic_label(q, data); |
79 | 0 | } |
80 | | |
81 | | int write_drop_in_format(const char *dir, const char *unit, unsigned level, |
82 | 0 | const char *name, const char *format, ...) { |
83 | 0 | _cleanup_free_ char *p = NULL; |
84 | 0 | va_list ap; |
85 | 0 | int r; |
86 | 0 |
|
87 | 0 | assert(dir); |
88 | 0 | assert(unit); |
89 | 0 | assert(name); |
90 | 0 | assert(format); |
91 | 0 |
|
92 | 0 | va_start(ap, format); |
93 | 0 | r = vasprintf(&p, format, ap); |
94 | 0 | va_end(ap); |
95 | 0 |
|
96 | 0 | if (r < 0) |
97 | 0 | return -ENOMEM; |
98 | 0 | |
99 | 0 | return write_drop_in(dir, unit, level, name, p); |
100 | 0 | } |
101 | | |
102 | | static int unit_file_find_dir( |
103 | | const char *original_root, |
104 | | const char *path, |
105 | 0 | char ***dirs) { |
106 | 0 |
|
107 | 0 | _cleanup_free_ char *chased = NULL; |
108 | 0 | int r; |
109 | 0 |
|
110 | 0 | assert(path); |
111 | 0 |
|
112 | 0 | r = chase_symlinks(path, original_root, 0, &chased); |
113 | 0 | if (r == -ENOENT) /* Ignore -ENOENT, after all most units won't have a drop-in dir. */ |
114 | 0 | return 0; |
115 | 0 | if (r == -ENAMETOOLONG) { |
116 | 0 | /* Also, ignore -ENAMETOOLONG but log about it. After all, users are not even able to create the |
117 | 0 | * drop-in dir in such case. This mostly happens for device units with an overly long /sys path. */ |
118 | 0 | log_debug_errno(r, "Path '%s' too long, couldn't canonicalize, ignoring.", path); |
119 | 0 | return 0; |
120 | 0 | } |
121 | 0 | if (r < 0) |
122 | 0 | return log_warning_errno(r, "Failed to canonicalize path '%s': %m", path); |
123 | 0 | |
124 | 0 | r = strv_push(dirs, chased); |
125 | 0 | if (r < 0) |
126 | 0 | return log_oom(); |
127 | 0 | |
128 | 0 | chased = NULL; |
129 | 0 | return 0; |
130 | 0 | } |
131 | | |
132 | | static int unit_file_find_dirs( |
133 | | const char *original_root, |
134 | | Set *unit_path_cache, |
135 | | const char *unit_path, |
136 | | const char *name, |
137 | | const char *suffix, |
138 | 0 | char ***dirs) { |
139 | 0 |
|
140 | 0 | _cleanup_free_ char *prefix = NULL, *instance = NULL, *built = NULL; |
141 | 0 | bool is_instance, chopped; |
142 | 0 | const char *dash; |
143 | 0 | UnitType type; |
144 | 0 | char *path; |
145 | 0 | size_t n; |
146 | 0 | int r; |
147 | 0 |
|
148 | 0 | assert(unit_path); |
149 | 0 | assert(name); |
150 | 0 | assert(suffix); |
151 | 0 |
|
152 | 0 | path = strjoina(unit_path, "/", name, suffix); |
153 | 0 | if (!unit_path_cache || set_get(unit_path_cache, path)) { |
154 | 0 | r = unit_file_find_dir(original_root, path, dirs); |
155 | 0 | if (r < 0) |
156 | 0 | return r; |
157 | 0 | } |
158 | 0 | |
159 | 0 | is_instance = unit_name_is_valid(name, UNIT_NAME_INSTANCE); |
160 | 0 | if (is_instance) { /* Also try the template dir */ |
161 | 0 | _cleanup_free_ char *template = NULL; |
162 | 0 |
|
163 | 0 | r = unit_name_template(name, &template); |
164 | 0 | if (r < 0) |
165 | 0 | return log_error_errno(r, "Failed to generate template from unit name: %m"); |
166 | 0 | |
167 | 0 | r = unit_file_find_dirs(original_root, unit_path_cache, unit_path, template, suffix, dirs); |
168 | 0 | if (r < 0) |
169 | 0 | return r; |
170 | 0 | } |
171 | 0 | |
172 | 0 | /* Let's see if there's a "-" prefix for this unit name. If so, let's invoke ourselves for it. This will then |
173 | 0 | * recursively do the same for all our prefixes. i.e. this means given "foo-bar-waldo.service" we'll also |
174 | 0 | * search "foo-bar-.service" and "foo-.service". |
175 | 0 | * |
176 | 0 | * Note the order in which we do it: we traverse up adding drop-ins on each step. This means the more specific |
177 | 0 | * drop-ins may override the more generic drop-ins, which is the intended behaviour. */ |
178 | 0 | |
179 | 0 | r = unit_name_to_prefix(name, &prefix); |
180 | 0 | if (r < 0) |
181 | 0 | return log_error_errno(r, "Failed to derive unit name prefix from unit name: %m"); |
182 | 0 | |
183 | 0 | chopped = false; |
184 | 0 | for (;;) { |
185 | 0 | dash = strrchr(prefix, '-'); |
186 | 0 | if (!dash) /* No dash? if so we are done */ |
187 | 0 | return 0; |
188 | 0 | |
189 | 0 | n = (size_t) (dash - prefix); |
190 | 0 | if (n == 0) /* Leading dash? If so, we are done */ |
191 | 0 | return 0; |
192 | 0 | |
193 | 0 | if (prefix[n+1] != 0 || chopped) { |
194 | 0 | prefix[n+1] = 0; |
195 | 0 | break; |
196 | 0 | } |
197 | 0 | |
198 | 0 | /* Trailing dash? If so, chop it off and try again, but not more than once. */ |
199 | 0 | prefix[n] = 0; |
200 | 0 | chopped = true; |
201 | 0 | } |
202 | 0 |
|
203 | 0 | if (!unit_prefix_is_valid(prefix)) |
204 | 0 | return 0; |
205 | 0 | |
206 | 0 | type = unit_name_to_type(name); |
207 | 0 | if (type < 0) |
208 | 0 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), |
209 | 0 | "Failed to to derive unit type from unit name: %s", |
210 | 0 | name); |
211 | 0 | |
212 | 0 | if (is_instance) { |
213 | 0 | r = unit_name_to_instance(name, &instance); |
214 | 0 | if (r < 0) |
215 | 0 | return log_error_errno(r, "Failed to derive unit name instance from unit name: %m"); |
216 | 0 | } |
217 | 0 | |
218 | 0 | r = unit_name_build_from_type(prefix, instance, type, &built); |
219 | 0 | if (r < 0) |
220 | 0 | return log_error_errno(r, "Failed to build prefix unit name: %m"); |
221 | 0 | |
222 | 0 | return unit_file_find_dirs(original_root, unit_path_cache, unit_path, built, suffix, dirs); |
223 | 0 | } |
224 | | |
225 | | int unit_file_find_dropin_paths( |
226 | | const char *original_root, |
227 | | char **lookup_path, |
228 | | Set *unit_path_cache, |
229 | | const char *dir_suffix, |
230 | | const char *file_suffix, |
231 | | Set *names, |
232 | 29.0k | char ***ret) { |
233 | 29.0k | |
234 | 29.0k | _cleanup_strv_free_ char **dirs = NULL; |
235 | 29.0k | char *t, **p; |
236 | 29.0k | Iterator i; |
237 | 29.0k | int r; |
238 | 29.0k | |
239 | 29.0k | assert(ret); |
240 | 29.0k | |
241 | 29.0k | SET_FOREACH(t, names, i) |
242 | 29.0k | STRV_FOREACH(p, lookup_path) |
243 | 29.0k | (void) unit_file_find_dirs(original_root, unit_path_cache, *p, t, dir_suffix, &dirs); |
244 | 29.0k | |
245 | 29.0k | if (strv_isempty(dirs)) { |
246 | 29.0k | *ret = NULL; |
247 | 29.0k | return 0; |
248 | 29.0k | } |
249 | 0 | |
250 | 0 | r = conf_files_list_strv(ret, file_suffix, NULL, 0, (const char**) dirs); |
251 | 0 | if (r < 0) |
252 | 0 | return log_warning_errno(r, "Failed to create the list of configuration files: %m"); |
253 | 0 | |
254 | 0 | return 1; |
255 | 0 | } |