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