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