]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
8b835fcc ZJS |
2 | |
3 | #include <stdlib.h> | |
8b835fcc | 4 | |
b5efdb8a | 5 | #include "alloc-util.h" |
57b7a260 | 6 | #include "all-units.h" |
5f311f8c | 7 | #include "analyze-verify.h" |
4bd29fe5 | 8 | #include "bus-error.h" |
8b835fcc ZJS |
9 | #include "bus-util.h" |
10 | #include "log.h" | |
5f311f8c | 11 | #include "manager.h" |
78002a67 | 12 | #include "pager.h" |
5f311f8c LP |
13 | #include "path-util.h" |
14 | #include "strv.h" | |
25f17e47 | 15 | #include "unit-name.h" |
2d3b784d | 16 | #include "unit-serialize.h" |
25f17e47 EV |
17 | |
18 | static int prepare_filename(const char *filename, char **ret) { | |
19 | int r; | |
20 | const char *name; | |
21 | _cleanup_free_ char *abspath = NULL; | |
22 | _cleanup_free_ char *dir = NULL; | |
23 | _cleanup_free_ char *with_instance = NULL; | |
24 | char *c; | |
25 | ||
26 | assert(filename); | |
27 | assert(ret); | |
28 | ||
29 | r = path_make_absolute_cwd(filename, &abspath); | |
30 | if (r < 0) | |
31 | return r; | |
32 | ||
33 | name = basename(abspath); | |
34 | if (!unit_name_is_valid(name, UNIT_NAME_ANY)) | |
35 | return -EINVAL; | |
36 | ||
37 | if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { | |
38 | r = unit_name_replace_instance(name, "i", &with_instance); | |
39 | if (r < 0) | |
40 | return r; | |
41 | } | |
42 | ||
43 | dir = dirname_malloc(abspath); | |
44 | if (!dir) | |
45 | return -ENOMEM; | |
46 | ||
62a85ee0 | 47 | c = path_join(dir, with_instance ?: name); |
25f17e47 EV |
48 | if (!c) |
49 | return -ENOMEM; | |
50 | ||
51 | *ret = c; | |
52 | return 0; | |
53 | } | |
8b835fcc ZJS |
54 | |
55 | static int generate_path(char **var, char **filenames) { | |
d941ea22 | 56 | const char *old; |
8b835fcc ZJS |
57 | char **filename; |
58 | ||
59 | _cleanup_strv_free_ char **ans = NULL; | |
60 | int r; | |
61 | ||
62 | STRV_FOREACH(filename, filenames) { | |
63 | char *t; | |
64 | ||
65 | t = dirname_malloc(*filename); | |
66 | if (!t) | |
67 | return -ENOMEM; | |
68 | ||
69 | r = strv_consume(&ans, t); | |
70 | if (r < 0) | |
71 | return r; | |
72 | } | |
73 | ||
74 | assert_se(strv_uniq(ans)); | |
75 | ||
d941ea22 ZJS |
76 | /* First, prepend our directories. Second, if some path was specified, use that, and |
77 | * otherwise use the defaults. Any duplicates will be filtered out in path-lookup.c. | |
78 | * Treat explicit empty path to mean that nothing should be appended. | |
79 | */ | |
80 | old = getenv("SYSTEMD_UNIT_PATH"); | |
81 | if (!streq_ptr(old, "")) { | |
82 | if (!old) | |
83 | old = ":"; | |
84 | ||
85 | r = strv_extend(&ans, old); | |
86 | if (r < 0) | |
87 | return r; | |
88 | } | |
8b835fcc ZJS |
89 | |
90 | *var = strv_join(ans, ":"); | |
91 | if (!*var) | |
92 | return -ENOMEM; | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | static int verify_socket(Unit *u) { | |
934ef6a5 | 98 | Unit *service; |
8b835fcc ZJS |
99 | int r; |
100 | ||
101 | assert(u); | |
102 | ||
103 | if (u->type != UNIT_SOCKET) | |
104 | return 0; | |
105 | ||
934ef6a5 | 106 | r = socket_load_service_unit(SOCKET(u), -1, &service); |
4d2a7ffd | 107 | if (r < 0) |
934ef6a5 | 108 | return log_unit_error_errno(u, r, "service unit for the socket cannot be loaded: %m"); |
8b835fcc | 109 | |
934ef6a5 ZJS |
110 | if (service->load_state != UNIT_LOADED) |
111 | return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), | |
112 | "service %s not loaded, socket cannot be started.", service->id); | |
8b835fcc | 113 | |
934ef6a5 | 114 | log_unit_debug(u, "using service unit %s.", service->id); |
8b835fcc ZJS |
115 | return 0; |
116 | } | |
117 | ||
c90c597e | 118 | int verify_executable(Unit *u, const ExecCommand *exec) { |
b9b442a0 YW |
119 | int r; |
120 | ||
234519ae | 121 | if (!exec) |
8b835fcc ZJS |
122 | return 0; |
123 | ||
c90c597e GS |
124 | if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE) |
125 | return 0; | |
126 | ||
b9b442a0 YW |
127 | r = find_executable_full(exec->path, false, NULL, NULL); |
128 | if (r < 0) | |
129 | return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path); | |
8b835fcc ZJS |
130 | |
131 | return 0; | |
132 | } | |
133 | ||
134 | static int verify_executables(Unit *u) { | |
135 | ExecCommand *exec; | |
136 | int r = 0, k; | |
137 | unsigned i; | |
138 | ||
139 | assert(u); | |
140 | ||
141 | exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command : | |
142 | u->type == UNIT_MOUNT ? MOUNT(u)->control_command : | |
143 | u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL; | |
144 | k = verify_executable(u, exec); | |
145 | if (k < 0 && r == 0) | |
146 | r = k; | |
147 | ||
148 | if (u->type == UNIT_SERVICE) | |
149 | for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) { | |
150 | k = verify_executable(u, SERVICE(u)->exec_command[i]); | |
151 | if (k < 0 && r == 0) | |
152 | r = k; | |
153 | } | |
154 | ||
155 | if (u->type == UNIT_SOCKET) | |
156 | for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) { | |
157 | k = verify_executable(u, SOCKET(u)->exec_command[i]); | |
158 | if (k < 0 && r == 0) | |
159 | r = k; | |
160 | } | |
161 | ||
162 | return r; | |
163 | } | |
164 | ||
1d3bc017 | 165 | static int verify_documentation(Unit *u, bool check_man) { |
78002a67 ZJS |
166 | char **p; |
167 | int r = 0, k; | |
168 | ||
78002a67 | 169 | STRV_FOREACH(p, u->documentation) { |
f2341e0a LP |
170 | log_unit_debug(u, "Found documentation item: %s", *p); |
171 | ||
1d3bc017 | 172 | if (check_man && startswith(*p, "man:")) { |
78002a67 ZJS |
173 | k = show_man_page(*p + 4, true); |
174 | if (k != 0) { | |
175 | if (k < 0) | |
e95a86c7 | 176 | log_unit_error_errno(u, k, "Can't show %s: %m", *p + 4); |
78002a67 | 177 | else { |
e95a86c7 | 178 | log_unit_error(u, "Command 'man %s' failed with code %d", *p + 4, k); |
78002a67 ZJS |
179 | k = -ENOEXEC; |
180 | } | |
181 | if (r == 0) | |
182 | r = k; | |
183 | } | |
184 | } | |
185 | } | |
186 | ||
187 | /* Check remote URLs? */ | |
188 | ||
189 | return r; | |
190 | } | |
191 | ||
1d3bc017 | 192 | static int verify_unit(Unit *u, bool check_man) { |
4afd3348 | 193 | _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; |
8b835fcc ZJS |
194 | int r, k; |
195 | ||
196 | assert(u); | |
197 | ||
f1d34068 | 198 | if (DEBUG_LOGGING) |
8b835fcc ZJS |
199 | unit_dump(u, stdout, "\t"); |
200 | ||
f2341e0a | 201 | log_unit_debug(u, "Creating %s/start job", u->id); |
50cbaba4 | 202 | r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, NULL, &err, NULL); |
8b835fcc | 203 | if (r < 0) |
4bd29fe5 | 204 | log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r)); |
8b835fcc ZJS |
205 | |
206 | k = verify_socket(u); | |
207 | if (k < 0 && r == 0) | |
208 | r = k; | |
209 | ||
210 | k = verify_executables(u); | |
211 | if (k < 0 && r == 0) | |
212 | r = k; | |
213 | ||
1d3bc017 | 214 | k = verify_documentation(u, check_man); |
78002a67 ZJS |
215 | if (k < 0 && r == 0) |
216 | r = k; | |
217 | ||
8b835fcc ZJS |
218 | return r; |
219 | } | |
220 | ||
641c0fd1 | 221 | int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators) { |
638cece4 LP |
222 | const ManagerTestRunFlags flags = |
223 | MANAGER_TEST_RUN_BASIC | | |
224 | MANAGER_TEST_RUN_ENV_GENERATORS | | |
225 | run_generators * MANAGER_TEST_RUN_GENERATORS; | |
8b835fcc | 226 | |
b5c9144c LP |
227 | _cleanup_(manager_freep) Manager *m = NULL; |
228 | Unit *units[strv_length(filenames)]; | |
229 | _cleanup_free_ char *var = NULL; | |
6f32f8e0 | 230 | int r, k, i, count = 0; |
b5c9144c LP |
231 | char **filename; |
232 | ||
1d3bc017 ZJS |
233 | if (strv_isempty(filenames)) |
234 | return 0; | |
235 | ||
8b835fcc ZJS |
236 | /* set the path */ |
237 | r = generate_path(&var, filenames); | |
23bbb0de MS |
238 | if (r < 0) |
239 | return log_error_errno(r, "Failed to generate unit load path: %m"); | |
8b835fcc ZJS |
240 | |
241 | assert_se(set_unit_path(var) >= 0); | |
242 | ||
641c0fd1 | 243 | r = manager_new(scope, flags, &m); |
23bbb0de | 244 | if (r < 0) |
ff9b60f3 | 245 | return log_error_errno(r, "Failed to initialize manager: %m"); |
8b835fcc ZJS |
246 | |
247 | log_debug("Starting manager..."); | |
248 | ||
76752516 | 249 | r = manager_startup(m, NULL, NULL); |
b5c9144c | 250 | if (r < 0) |
572986ca | 251 | return r; |
8b835fcc ZJS |
252 | |
253 | manager_clear_jobs(m); | |
254 | ||
255 | log_debug("Loading remaining units from the command line..."); | |
256 | ||
257 | STRV_FOREACH(filename, filenames) { | |
25f17e47 | 258 | _cleanup_free_ char *prepared = NULL; |
1d3bc017 | 259 | |
8b835fcc ZJS |
260 | log_debug("Handling %s...", *filename); |
261 | ||
25f17e47 EV |
262 | k = prepare_filename(*filename, &prepared); |
263 | if (k < 0) { | |
264 | log_error_errno(k, "Failed to prepare filename %s: %m", *filename); | |
265 | if (r == 0) | |
266 | r = k; | |
267 | continue; | |
268 | } | |
269 | ||
f79cd1a9 | 270 | k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]); |
fc9de36a HD |
271 | if (k < 0) { |
272 | if (r == 0) | |
273 | r = k; | |
274 | continue; | |
275 | } | |
276 | ||
277 | count++; | |
8b835fcc ZJS |
278 | } |
279 | ||
280 | for (i = 0; i < count; i++) { | |
1d3bc017 | 281 | k = verify_unit(units[i], check_man); |
8b835fcc ZJS |
282 | if (k < 0 && r == 0) |
283 | r = k; | |
284 | } | |
285 | ||
8b835fcc ZJS |
286 | return r; |
287 | } |