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