chiark / gitweb /
man: add systemd-verify(1)
[elogind.git] / src / verify / verify.c
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"
30 #include "pager.h"
31
32 SystemdRunningAs arg_running_as = SYSTEMD_SYSTEM;
33 bool arg_no_man = false;
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
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
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
203         k = verify_documentation(u);
204         if (k < 0 && r == 0)
205                 r = k;
206
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"
281                "     --system         Test system units\n"
282                "     --user           Test user units\n"
283                "     --no-man         Do not check for existence of man pages\n"
284                , program_invocation_short_name);
285 }
286
287 static int parse_argv(int argc, char *argv[]) {
288         enum {
289                 ARG_VERSION = 0x100,
290                 ARG_USER,
291                 ARG_SYSTEM,
292                 ARG_NO_MAN,
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
330                 case ARG_NO_MAN:
331                         arg_no_man = true;
332                         break;
333
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 }