]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/analyze/analyze-verify.c
verify: use manager_load_startable_unit_or_warn() to load units for verification
[thirdparty/systemd.git] / src / analyze / analyze-verify.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2014 Zbigniew Jędrzejewski-Szmek
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <stdlib.h>
22
23 #include "alloc-util.h"
24 #include "analyze-verify.h"
25 #include "bus-error.h"
26 #include "bus-util.h"
27 #include "log.h"
28 #include "manager.h"
29 #include "pager.h"
30 #include "path-util.h"
31 #include "strv.h"
32 #include "unit-name.h"
33
34 static int prepare_filename(const char *filename, char **ret) {
35 int r;
36 const char *name;
37 _cleanup_free_ char *abspath = NULL;
38 _cleanup_free_ char *dir = NULL;
39 _cleanup_free_ char *with_instance = NULL;
40 char *c;
41
42 assert(filename);
43 assert(ret);
44
45 r = path_make_absolute_cwd(filename, &abspath);
46 if (r < 0)
47 return r;
48
49 name = basename(abspath);
50 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
51 return -EINVAL;
52
53 if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
54 r = unit_name_replace_instance(name, "i", &with_instance);
55 if (r < 0)
56 return r;
57 }
58
59 dir = dirname_malloc(abspath);
60 if (!dir)
61 return -ENOMEM;
62
63 if (with_instance)
64 c = path_join(NULL, dir, with_instance);
65 else
66 c = path_join(NULL, dir, name);
67 if (!c)
68 return -ENOMEM;
69
70 *ret = c;
71 return 0;
72 }
73
74 static int generate_path(char **var, char **filenames) {
75 const char *old;
76 char **filename;
77
78 _cleanup_strv_free_ char **ans = NULL;
79 int r;
80
81 STRV_FOREACH(filename, filenames) {
82 char *t;
83
84 t = dirname_malloc(*filename);
85 if (!t)
86 return -ENOMEM;
87
88 r = strv_consume(&ans, t);
89 if (r < 0)
90 return r;
91 }
92
93 assert_se(strv_uniq(ans));
94
95 /* First, prepend our directories. Second, if some path was specified, use that, and
96 * otherwise use the defaults. Any duplicates will be filtered out in path-lookup.c.
97 * Treat explicit empty path to mean that nothing should be appended.
98 */
99 old = getenv("SYSTEMD_UNIT_PATH");
100 if (!streq_ptr(old, "")) {
101 if (!old)
102 old = ":";
103
104 r = strv_extend(&ans, old);
105 if (r < 0)
106 return r;
107 }
108
109 *var = strv_join(ans, ":");
110 if (!*var)
111 return -ENOMEM;
112
113 return 0;
114 }
115
116 static int verify_socket(Unit *u) {
117 int r;
118
119 assert(u);
120
121 if (u->type != UNIT_SOCKET)
122 return 0;
123
124 /* Cannot run this without the service being around */
125
126 /* This makes sure instance is created if necessary. */
127 r = socket_instantiate_service(SOCKET(u));
128 if (r < 0) {
129 log_unit_error_errno(u, r, "Socket cannot be started, failed to create instance: %m");
130 return r;
131 }
132
133 /* This checks both type of sockets */
134 if (UNIT_ISSET(SOCKET(u)->service)) {
135 Service *service;
136
137 service = SERVICE(UNIT_DEREF(SOCKET(u)->service));
138 log_unit_debug(u, "Using %s", UNIT(service)->id);
139
140 if (UNIT(service)->load_state != UNIT_LOADED) {
141 log_unit_error(u, "Service %s not loaded, %s cannot be started.", UNIT(service)->id, u->id);
142 return -ENOENT;
143 }
144 }
145
146 return 0;
147 }
148
149 static int verify_executable(Unit *u, ExecCommand *exec) {
150 if (!exec)
151 return 0;
152
153 if (access(exec->path, X_OK) < 0)
154 return log_unit_error_errno(u, errno, "Command %s is not executable: %m", exec->path);
155
156 return 0;
157 }
158
159 static int verify_executables(Unit *u) {
160 ExecCommand *exec;
161 int r = 0, k;
162 unsigned i;
163
164 assert(u);
165
166 exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command :
167 u->type == UNIT_MOUNT ? MOUNT(u)->control_command :
168 u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL;
169 k = verify_executable(u, exec);
170 if (k < 0 && r == 0)
171 r = k;
172
173 if (u->type == UNIT_SERVICE)
174 for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) {
175 k = verify_executable(u, SERVICE(u)->exec_command[i]);
176 if (k < 0 && r == 0)
177 r = k;
178 }
179
180 if (u->type == UNIT_SOCKET)
181 for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) {
182 k = verify_executable(u, SOCKET(u)->exec_command[i]);
183 if (k < 0 && r == 0)
184 r = k;
185 }
186
187 return r;
188 }
189
190 static int verify_documentation(Unit *u, bool check_man) {
191 char **p;
192 int r = 0, k;
193
194 STRV_FOREACH(p, u->documentation) {
195 log_unit_debug(u, "Found documentation item: %s", *p);
196
197 if (check_man && startswith(*p, "man:")) {
198 k = show_man_page(*p + 4, true);
199 if (k != 0) {
200 if (k < 0)
201 log_unit_error_errno(u, r, "Can't show %s: %m", *p);
202 else {
203 log_unit_error_errno(u, r, "man %s command failed with code %d", *p + 4, k);
204 k = -ENOEXEC;
205 }
206 if (r == 0)
207 r = k;
208 }
209 }
210 }
211
212 /* Check remote URLs? */
213
214 return r;
215 }
216
217 static int verify_unit(Unit *u, bool check_man) {
218 _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
219 int r, k;
220
221 assert(u);
222
223 if (DEBUG_LOGGING)
224 unit_dump(u, stdout, "\t");
225
226 log_unit_debug(u, "Creating %s/start job", u->id);
227 r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, &err, NULL);
228 if (r < 0)
229 log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r));
230
231 k = verify_socket(u);
232 if (k < 0 && r == 0)
233 r = k;
234
235 k = verify_executables(u);
236 if (k < 0 && r == 0)
237 r = k;
238
239 k = verify_documentation(u, check_man);
240 if (k < 0 && r == 0)
241 r = k;
242
243 return r;
244 }
245
246 int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators) {
247 _cleanup_free_ char *var = NULL;
248 Manager *m = NULL;
249 FILE *serial = NULL;
250 FDSet *fdset = NULL;
251 char **filename;
252 int r = 0, k;
253
254 Unit *units[strv_length(filenames)];
255 int i, count = 0;
256 const uint8_t flags = MANAGER_TEST_RUN_BASIC |
257 MANAGER_TEST_RUN_ENV_GENERATORS |
258 run_generators * MANAGER_TEST_RUN_GENERATORS;
259
260 if (strv_isempty(filenames))
261 return 0;
262
263 /* set the path */
264 r = generate_path(&var, filenames);
265 if (r < 0)
266 return log_error_errno(r, "Failed to generate unit load path: %m");
267
268 assert_se(set_unit_path(var) >= 0);
269
270 r = manager_new(scope, flags, &m);
271 if (r < 0)
272 return log_error_errno(r, "Failed to initialize manager: %m");
273
274 log_debug("Starting manager...");
275
276 r = manager_startup(m, serial, fdset);
277 if (r < 0) {
278 log_error_errno(r, "Failed to start manager: %m");
279 goto finish;
280 }
281
282 manager_clear_jobs(m);
283
284 log_debug("Loading remaining units from the command line...");
285
286 STRV_FOREACH(filename, filenames) {
287 _cleanup_free_ char *prepared = NULL;
288
289 log_debug("Handling %s...", *filename);
290
291 k = prepare_filename(*filename, &prepared);
292 if (k < 0) {
293 log_error_errno(k, "Failed to prepare filename %s: %m", *filename);
294 if (r == 0)
295 r = k;
296 continue;
297 }
298
299 k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]);
300 if (k < 0 && r == 0)
301 r = k;
302 else
303 count++;
304 }
305
306 for (i = 0; i < count; i++) {
307 k = verify_unit(units[i], check_man);
308 if (k < 0 && r == 0)
309 r = k;
310 }
311
312 finish:
313 manager_free(m);
314
315 return r;
316 }