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 : }
|