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