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 4 : static int unit_file_add_dir(
97 : const char *original_root,
98 : const char *path,
99 : char ***dirs) {
100 :
101 4 : _cleanup_free_ char *chased = NULL;
102 : int r;
103 :
104 4 : assert(path);
105 :
106 : /* This adds [original_root]/path to dirs, if it exists. */
107 :
108 4 : r = chase_symlinks(path, original_root, 0, &chased);
109 4 : if (r == -ENOENT) /* Ignore -ENOENT, after all most units won't have a drop-in dir. */
110 0 : return 0;
111 4 : 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 4 : if (r < 0)
118 0 : return log_warning_errno(r, "Failed to canonicalize path '%s': %m", path);
119 :
120 4 : if (strv_consume(dirs, TAKE_PTR(chased)) < 0)
121 0 : return log_oom();
122 :
123 4 : return 0;
124 : }
125 :
126 24729 : 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 24729 : _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 24729 : assert(unit_path);
143 24729 : assert(name);
144 24729 : assert(suffix);
145 :
146 222561 : path = strjoina(unit_path, "/", name, suffix);
147 24729 : if (!unit_path_cache || set_get(unit_path_cache, path)) {
148 4 : r = unit_file_add_dir(original_root, path, dirs);
149 4 : if (r < 0)
150 0 : return r;
151 : }
152 :
153 24729 : is_instance = unit_name_is_valid(name, UNIT_NAME_INSTANCE);
154 24729 : 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 24729 : r = unit_name_to_prefix(name, &prefix);
174 24729 : if (r < 0)
175 0 : return log_error_errno(r, "Failed to derive unit name prefix from unit name: %m");
176 :
177 24729 : chopped = false;
178 : for (;;) {
179 61205 : dash = strrchr(prefix, '-');
180 42967 : if (!dash) /* No dash? if so we are done */
181 6413 : return 0;
182 :
183 36554 : n = (size_t) (dash - prefix);
184 36554 : if (n == 0) /* Leading dash? If so, we are done */
185 78 : return 0;
186 :
187 36476 : if (prefix[n+1] != 0 || chopped) {
188 18238 : prefix[n+1] = 0;
189 18238 : break;
190 : }
191 :
192 : /* Trailing dash? If so, chop it off and try again, but not more than once. */
193 18238 : prefix[n] = 0;
194 18238 : chopped = true;
195 : }
196 :
197 18238 : if (!unit_prefix_is_valid(prefix))
198 0 : return 0;
199 :
200 18238 : type = unit_name_to_type(name);
201 18238 : 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 18238 : 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 18238 : r = unit_name_build_from_type(prefix, instance, type, &built);
213 18238 : if (r < 0)
214 0 : return log_error_errno(r, "Failed to build prefix unit name: %m");
215 :
216 18238 : return unit_file_find_dirs(original_root, unit_path_cache, unit_path, built, suffix, dirs);
217 : }
218 :
219 6383 : 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 6383 : _cleanup_strv_free_ char **dirs = NULL;
229 : char *name, **p;
230 : Iterator i;
231 : int r;
232 :
233 6383 : assert(ret);
234 :
235 12874 : SET_FOREACH(name, names, i)
236 12982 : STRV_FOREACH(p, lookup_path)
237 6491 : (void) unit_file_find_dirs(original_root, unit_path_cache, *p, name, dir_suffix, &dirs);
238 :
239 6383 : if (strv_isempty(dirs)) {
240 6382 : *ret = NULL;
241 6382 : return 0;
242 : }
243 :
244 1 : r = conf_files_list_strv(ret, file_suffix, NULL, 0, (const char**) dirs);
245 1 : if (r < 0)
246 0 : return log_warning_errno(r, "Failed to create the list of configuration files: %m");
247 :
248 1 : return 1;
249 : }
|