]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze-verify.c
Merge pull request #18481 from keszybz/rpm-restart-post-trans
[thirdparty/systemd.git] / src / analyze / analyze-verify.c
CommitLineData
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
18static 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
55static 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
97static 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 118int 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
134static 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 165static 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 192static 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 221int 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}