]>
Commit | Line | Data |
---|---|---|
8b835fcc ZJS |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2014 Zbigniew Jędrzejewski-Szmek | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <stdlib.h> | |
23 | #include <getopt.h> | |
24 | ||
25 | #include "manager.h" | |
26 | #include "bus-util.h" | |
27 | #include "log.h" | |
28 | #include "strv.h" | |
29 | #include "build.h" | |
78002a67 | 30 | #include "pager.h" |
8b835fcc ZJS |
31 | |
32 | SystemdRunningAs arg_running_as = SYSTEMD_SYSTEM; | |
78002a67 | 33 | bool arg_no_man = false; |
8b835fcc ZJS |
34 | |
35 | static int generate_path(char **var, char **filenames) { | |
36 | char **filename; | |
37 | ||
38 | _cleanup_strv_free_ char **ans = NULL; | |
39 | int r; | |
40 | ||
41 | STRV_FOREACH(filename, filenames) { | |
42 | char *t; | |
43 | ||
44 | t = dirname_malloc(*filename); | |
45 | if (!t) | |
46 | return -ENOMEM; | |
47 | ||
48 | r = strv_consume(&ans, t); | |
49 | if (r < 0) | |
50 | return r; | |
51 | } | |
52 | ||
53 | assert_se(strv_uniq(ans)); | |
54 | ||
55 | r = strv_extend(&ans, ""); | |
56 | if (r < 0) | |
57 | return r; | |
58 | ||
59 | *var = strv_join(ans, ":"); | |
60 | if (!*var) | |
61 | return -ENOMEM; | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
66 | static int verify_socket(Unit *u) { | |
67 | int r; | |
68 | ||
69 | assert(u); | |
70 | ||
71 | if (u->type != UNIT_SOCKET) | |
72 | return 0; | |
73 | ||
74 | /* Cannot run this without the service being around */ | |
75 | ||
76 | /* This makes sure instance is created if necessary. */ | |
77 | r = socket_instantiate_service(SOCKET(u)); | |
78 | if (r < 0) { | |
79 | log_error_unit(u->id, "Socket %s cannot be started, failed to create instance.", | |
80 | u->id); | |
81 | return r; | |
82 | } | |
83 | ||
84 | /* This checks both type of sockets */ | |
85 | if (UNIT_ISSET(SOCKET(u)->service)) { | |
86 | Service *service; | |
87 | ||
88 | service = SERVICE(UNIT_DEREF(SOCKET(u)->service)); | |
89 | log_debug_unit(u->id, "%s uses %s", u->id, UNIT(service)->id); | |
90 | ||
91 | if (UNIT(service)->load_state != UNIT_LOADED) { | |
92 | log_error_unit(u->id, "Service %s not loaded, %s cannot be started.", | |
93 | UNIT(service)->id, u->id); | |
94 | return -ENOENT; | |
95 | } | |
96 | } | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | static int verify_executable(Unit *u, ExecCommand *exec) { | |
102 | if (exec == NULL) | |
103 | return 0; | |
104 | ||
105 | if (access(exec->path, X_OK) < 0) { | |
106 | log_error_unit(u->id, "%s: command %s is not executable: %m", | |
107 | u->id, exec->path); | |
108 | return -errno; | |
109 | } | |
110 | ||
111 | return 0; | |
112 | } | |
113 | ||
114 | static int verify_executables(Unit *u) { | |
115 | ExecCommand *exec; | |
116 | int r = 0, k; | |
117 | unsigned i; | |
118 | ||
119 | assert(u); | |
120 | ||
121 | exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command : | |
122 | u->type == UNIT_MOUNT ? MOUNT(u)->control_command : | |
123 | u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL; | |
124 | k = verify_executable(u, exec); | |
125 | if (k < 0 && r == 0) | |
126 | r = k; | |
127 | ||
128 | if (u->type == UNIT_SERVICE) | |
129 | for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) { | |
130 | k = verify_executable(u, SERVICE(u)->exec_command[i]); | |
131 | if (k < 0 && r == 0) | |
132 | r = k; | |
133 | } | |
134 | ||
135 | if (u->type == UNIT_SOCKET) | |
136 | for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) { | |
137 | k = verify_executable(u, SOCKET(u)->exec_command[i]); | |
138 | if (k < 0 && r == 0) | |
139 | r = k; | |
140 | } | |
141 | ||
142 | return r; | |
143 | } | |
144 | ||
78002a67 ZJS |
145 | static int verify_documentation(Unit *u) { |
146 | char **p; | |
147 | int r = 0, k; | |
148 | ||
149 | if (arg_no_man) | |
150 | return 0; | |
151 | ||
152 | STRV_FOREACH(p, u->documentation) { | |
153 | log_debug_unit(u->id, "%s: found documentation item %s.", u->id, *p); | |
154 | if (startswith(*p, "man:")) { | |
155 | k = show_man_page(*p + 4, true); | |
156 | if (k != 0) { | |
157 | if (k < 0) | |
158 | log_error_unit(u->id, "%s: can't show %s: %s", | |
159 | u->id, *p, strerror(-r)); | |
160 | else { | |
161 | log_error_unit(u->id, "%s: man %s command failed with code %d", | |
162 | u->id, *p + 4, k); | |
163 | k = -ENOEXEC; | |
164 | } | |
165 | if (r == 0) | |
166 | r = k; | |
167 | } | |
168 | } | |
169 | } | |
170 | ||
171 | /* Check remote URLs? */ | |
172 | ||
173 | return r; | |
174 | } | |
175 | ||
8b835fcc ZJS |
176 | static int test_unit(Unit *u) { |
177 | _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL; | |
178 | Job *j; | |
179 | int r, k; | |
180 | ||
181 | assert(u); | |
182 | ||
183 | if (log_get_max_level() >= LOG_DEBUG) | |
184 | unit_dump(u, stdout, "\t"); | |
185 | ||
186 | log_debug_unit(u->id, "Creating %s/start job", u->id); | |
187 | r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, false, &err, &j); | |
188 | if (sd_bus_error_is_set(&err)) | |
189 | log_error_unit(u->id, "Error: %s: %s", | |
190 | err.name, err.message); | |
191 | if (r < 0) | |
192 | log_error_unit(u->id, "Failed to create %s/start: %s", | |
193 | u->id, strerror(-r)); | |
194 | ||
195 | k = verify_socket(u); | |
196 | if (k < 0 && r == 0) | |
197 | r = k; | |
198 | ||
199 | k = verify_executables(u); | |
200 | if (k < 0 && r == 0) | |
201 | r = k; | |
202 | ||
78002a67 ZJS |
203 | k = verify_documentation(u); |
204 | if (k < 0 && r == 0) | |
205 | r = k; | |
206 | ||
8b835fcc ZJS |
207 | return r; |
208 | } | |
209 | ||
210 | static int test_units(char **filenames) { | |
211 | _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL; | |
212 | Manager *m = NULL; | |
213 | FILE *serial = NULL; | |
214 | FDSet *fdset = NULL; | |
215 | ||
216 | _cleanup_free_ char *var; | |
217 | ||
218 | char **filename; | |
219 | int r = 0, k; | |
220 | ||
221 | Unit *units[strv_length(filenames)]; | |
222 | int i, count = 0; | |
223 | ||
224 | /* set the path */ | |
225 | r = generate_path(&var, filenames); | |
226 | if (r < 0) { | |
227 | log_error("Failed to generate unit load path: %s", strerror(-r)); | |
228 | return r; | |
229 | } | |
230 | ||
231 | assert_se(set_unit_path(var) >= 0); | |
232 | ||
233 | r = manager_new(arg_running_as, true, &m); | |
234 | if (r < 0) { | |
235 | log_error("Failed to initalize manager: %s", strerror(-r)); | |
236 | return r; | |
237 | } | |
238 | ||
239 | log_debug("Starting manager..."); | |
240 | ||
241 | r = manager_startup(m, serial, fdset); | |
242 | if (r < 0) { | |
243 | log_error("Failed to start manager: %s", strerror(-r)); | |
244 | goto finish; | |
245 | } | |
246 | ||
247 | manager_clear_jobs(m); | |
248 | ||
249 | log_debug("Loading remaining units from the command line..."); | |
250 | ||
251 | STRV_FOREACH(filename, filenames) { | |
252 | log_debug("Handling %s...", *filename); | |
253 | ||
254 | k = manager_load_unit(m, NULL, *filename, &err, &units[count]); | |
255 | if (k < 0) { | |
256 | log_error("Failed to load %s: %s", *filename, strerror(-r)); | |
257 | if (r == 0) | |
258 | r = k; | |
259 | } | |
260 | ||
261 | count ++; | |
262 | } | |
263 | ||
264 | for (i = 0; i < count; i++) { | |
265 | k = test_unit(units[i]); | |
266 | if (k < 0 && r == 0) | |
267 | r = k; | |
268 | } | |
269 | ||
270 | finish: | |
271 | manager_free(m); | |
272 | ||
273 | return r; | |
274 | } | |
275 | ||
276 | static void help(void) { | |
277 | printf("%s [OPTIONS...] {COMMAND} ...\n\n" | |
278 | "Check if unit files can be correctly loaded.\n\n" | |
279 | " -h --help Show this help\n" | |
280 | " --version Show package version\n" | |
0e8415f2 ZJS |
281 | " --system Test system units\n" |
282 | " --user Test user units\n" | |
78002a67 ZJS |
283 | " --no-man Do not check for existence of man pages\n" |
284 | , program_invocation_short_name); | |
8b835fcc ZJS |
285 | } |
286 | ||
287 | static int parse_argv(int argc, char *argv[]) { | |
288 | enum { | |
289 | ARG_VERSION = 0x100, | |
290 | ARG_USER, | |
291 | ARG_SYSTEM, | |
78002a67 | 292 | ARG_NO_MAN, |
8b835fcc ZJS |
293 | }; |
294 | ||
295 | static const struct option options[] = { | |
296 | { "help", no_argument, NULL, 'h' }, | |
297 | { "version", no_argument, NULL, ARG_VERSION }, | |
298 | { "user", no_argument, NULL, ARG_USER }, | |
299 | { "system", no_argument, NULL, ARG_SYSTEM }, | |
300 | {} | |
301 | }; | |
302 | ||
303 | int c; | |
304 | ||
305 | assert(argc >= 1); | |
306 | assert(argv); | |
307 | ||
308 | opterr = 0; | |
309 | ||
310 | while ((c = getopt_long(argc, argv, ":h", options, NULL)) >= 0) | |
311 | switch (c) { | |
312 | ||
313 | case 'h': | |
314 | help(); | |
315 | return 0; | |
316 | ||
317 | case ARG_VERSION: | |
318 | puts(PACKAGE_STRING); | |
319 | puts(SYSTEMD_FEATURES); | |
320 | return 0; | |
321 | ||
322 | case ARG_USER: | |
323 | arg_running_as = SYSTEMD_USER; | |
324 | break; | |
325 | ||
326 | case ARG_SYSTEM: | |
327 | arg_running_as = SYSTEMD_SYSTEM; | |
328 | break; | |
329 | ||
78002a67 ZJS |
330 | case ARG_NO_MAN: |
331 | arg_no_man = true; | |
332 | break; | |
333 | ||
8b835fcc ZJS |
334 | case '?': |
335 | log_error("Unknown option %s.", argv[optind-1]); | |
336 | return -EINVAL; | |
337 | ||
338 | case ':': | |
339 | log_error("Missing argument to %s.", argv[optind-1]); | |
340 | return -EINVAL; | |
341 | ||
342 | default: | |
343 | assert_not_reached("Unhandled option code."); | |
344 | } | |
345 | ||
346 | return 1; /* work to do */ | |
347 | } | |
348 | ||
349 | int main(int argc, char *argv[]) { | |
350 | int r; | |
351 | ||
352 | log_parse_environment(); | |
353 | log_open(); | |
354 | ||
355 | r = parse_argv(argc, argv); | |
356 | if (r <= 0) | |
357 | goto finish; | |
358 | ||
359 | r = test_units(argv + optind); | |
360 | ||
361 | finish: | |
362 | return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; | |
363 | } |