]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
daf71ef6 | 2 | |
6553db60 | 3 | #include <fnmatch.h> |
daf71ef6 LP |
4 | #include <sys/reboot.h> |
5 | #include <unistd.h> | |
6 | ||
7 | #include "sd-bus.h" | |
8 | #include "sd-daemon.h" | |
9 | ||
a8c3ed5f | 10 | #include "ask-password-agent.h" |
daf71ef6 LP |
11 | #include "bus-common-errors.h" |
12 | #include "bus-locator.h" | |
13 | #include "bus-map-properties.h" | |
14 | #include "bus-unit-util.h" | |
1e35e81b | 15 | #include "bus-util.h" |
f461a28d | 16 | #include "chase.h" |
daf71ef6 LP |
17 | #include "dropin.h" |
18 | #include "env-util.h" | |
19 | #include "exit-status.h" | |
0bbd8e18 DDM |
20 | #include "format-table.h" |
21 | #include "format-util.h" | |
daf71ef6 | 22 | #include "glob-util.h" |
0bbd8e18 DDM |
23 | #include "install.h" |
24 | #include "output-mode.h" | |
25 | #include "path-lookup.h" | |
daf71ef6 | 26 | #include "path-util.h" |
716a4cdb | 27 | #include "pidref.h" |
1b919ca4 | 28 | #include "polkit-agent.h" |
716a4cdb | 29 | #include "process-util.h" |
daf71ef6 | 30 | #include "reboot-util.h" |
0bbd8e18 | 31 | #include "runtime-scope.h" |
daf71ef6 | 32 | #include "set.h" |
0bbd8e18 DDM |
33 | #include "string-util.h" |
34 | #include "strv.h" | |
daf71ef6 | 35 | #include "systemctl.h" |
1cf40697 | 36 | #include "systemctl-util.h" |
0bbd8e18 DDM |
37 | #include "unit-file.h" |
38 | #include "unit-name.h" | |
daf71ef6 LP |
39 | #include "verbs.h" |
40 | ||
41 | static sd_bus *buses[_BUS_FOCUS_MAX] = {}; | |
42 | ||
c577fe65 | 43 | int acquire_bus_full(BusFocus focus, bool graceful, sd_bus **ret) { |
daf71ef6 LP |
44 | int r; |
45 | ||
46 | assert(focus < _BUS_FOCUS_MAX); | |
47 | assert(ret); | |
48 | ||
d77d42ed YW |
49 | if (!IN_SET(arg_runtime_scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER)) |
50 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--global is not supported for this operation."); | |
51 | ||
daf71ef6 | 52 | /* We only go directly to the manager, if we are using a local transport */ |
56cb74c3 | 53 | if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)) |
daf71ef6 LP |
54 | focus = BUS_FULL; |
55 | ||
56 | if (getenv_bool("SYSTEMCTL_FORCE_BUS") > 0) | |
57 | focus = BUS_FULL; | |
58 | ||
59 | if (!buses[focus]) { | |
daf71ef6 | 60 | if (focus == BUS_MANAGER) |
4870133b | 61 | r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &buses[focus]); |
daf71ef6 | 62 | else |
4870133b | 63 | r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &buses[focus]); |
daf71ef6 | 64 | if (r < 0) |
c577fe65 YW |
65 | return bus_log_connect_full(graceful && focus == BUS_FULL && r == -ECONNREFUSED ? LOG_DEBUG : LOG_ERR, |
66 | r, arg_transport, arg_runtime_scope); | |
daf71ef6 LP |
67 | |
68 | (void) sd_bus_set_allow_interactive_authorization(buses[focus], arg_ask_password); | |
69 | } | |
70 | ||
71 | *ret = buses[focus]; | |
72 | return 0; | |
73 | } | |
74 | ||
75 | void release_busses(void) { | |
848cdaa6 MY |
76 | FOREACH_ARRAY(w, buses, _BUS_FOCUS_MAX) |
77 | *w = sd_bus_flush_close_unref(*w); | |
daf71ef6 LP |
78 | } |
79 | ||
80 | void ask_password_agent_open_maybe(void) { | |
81 | /* Open the password agent as a child process if necessary */ | |
82 | ||
83 | if (arg_dry_run) | |
84 | return; | |
85 | ||
4870133b | 86 | if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM) |
daf71ef6 LP |
87 | return; |
88 | ||
89 | ask_password_agent_open_if_enabled(arg_transport, arg_ask_password); | |
90 | } | |
91 | ||
92 | void polkit_agent_open_maybe(void) { | |
93 | /* Open the polkit agent as a child process if necessary */ | |
94 | ||
4870133b | 95 | if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM) |
daf71ef6 LP |
96 | return; |
97 | ||
f3cf6167 | 98 | (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); |
daf71ef6 LP |
99 | } |
100 | ||
101 | int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) { | |
102 | assert(error); | |
103 | ||
104 | if (!sd_bus_error_is_set(error)) | |
105 | return r; | |
106 | ||
107 | if (sd_bus_error_has_names(error, SD_BUS_ERROR_ACCESS_DENIED, | |
108 | BUS_ERROR_ONLY_BY_DEPENDENCY, | |
109 | BUS_ERROR_NO_ISOLATION, | |
110 | BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE)) | |
111 | return EXIT_NOPERMISSION; | |
112 | ||
113 | if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT)) | |
114 | return EXIT_NOTINSTALLED; | |
115 | ||
116 | if (sd_bus_error_has_names(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, | |
117 | SD_BUS_ERROR_NOT_SUPPORTED)) | |
118 | return EXIT_NOTIMPLEMENTED; | |
119 | ||
120 | if (sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) | |
121 | return EXIT_NOTCONFIGURED; | |
122 | ||
123 | if (r != 0) | |
124 | return r; | |
125 | ||
126 | return EXIT_FAILURE; | |
127 | } | |
128 | ||
129 | int get_state_one_unit(sd_bus *bus, const char *unit, UnitActiveState *ret_active_state) { | |
130 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
131 | _cleanup_free_ char *buf = NULL, *dbus_path = NULL; | |
132 | UnitActiveState state; | |
133 | int r; | |
134 | ||
37299769 | 135 | assert(bus); |
daf71ef6 LP |
136 | assert(unit); |
137 | assert(ret_active_state); | |
138 | ||
139 | dbus_path = unit_dbus_path_from_name(unit); | |
140 | if (!dbus_path) | |
141 | return log_oom(); | |
142 | ||
143 | r = sd_bus_get_property_string( | |
144 | bus, | |
145 | "org.freedesktop.systemd1", | |
146 | dbus_path, | |
147 | "org.freedesktop.systemd1.Unit", | |
148 | "ActiveState", | |
149 | &error, | |
150 | &buf); | |
151 | if (r < 0) | |
152 | return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r)); | |
153 | ||
154 | state = unit_active_state_from_string(buf); | |
155 | if (state < 0) | |
7211c853 | 156 | return log_error_errno(state, "Invalid unit state '%s' for: %s", buf, unit); |
daf71ef6 LP |
157 | |
158 | *ret_active_state = state; | |
159 | return 0; | |
160 | } | |
161 | ||
37299769 MY |
162 | int get_sub_state_one_unit(sd_bus *bus, const char *unit, char **ret_sub_state) { |
163 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
164 | _cleanup_free_ char *sub_state = NULL, *dbus_path = NULL; | |
165 | int r; | |
166 | ||
167 | assert(bus); | |
168 | assert(unit); | |
169 | assert(ret_sub_state); | |
170 | ||
171 | dbus_path = unit_dbus_path_from_name(unit); | |
172 | if (!dbus_path) | |
173 | return log_oom(); | |
174 | ||
175 | r = sd_bus_get_property_string( | |
176 | bus, | |
177 | "org.freedesktop.systemd1", | |
178 | dbus_path, | |
179 | "org.freedesktop.systemd1.Unit", | |
180 | "SubState", | |
181 | &error, | |
182 | &sub_state); | |
183 | if (r < 0) | |
184 | return log_error_errno(r, "Failed to retrieve unit sub state: %s", bus_error_message(&error, r)); | |
185 | ||
186 | *ret_sub_state = TAKE_PTR(sub_state); | |
187 | return 0; | |
188 | } | |
189 | ||
daf71ef6 LP |
190 | int get_unit_list( |
191 | sd_bus *bus, | |
192 | const char *machine, | |
193 | char **patterns, | |
194 | UnitInfo **unit_infos, | |
195 | int c, | |
196 | sd_bus_message **ret_reply) { | |
197 | ||
198 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; | |
199 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
200 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
daf71ef6 LP |
201 | int r; |
202 | bool fallback = false; | |
203 | ||
204 | assert(bus); | |
205 | assert(unit_infos); | |
206 | assert(ret_reply); | |
207 | ||
208 | r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsByPatterns"); | |
209 | if (r < 0) | |
210 | return bus_log_create_error(r); | |
211 | ||
212 | r = sd_bus_message_append_strv(m, arg_states); | |
213 | if (r < 0) | |
214 | return bus_log_create_error(r); | |
215 | ||
216 | r = sd_bus_message_append_strv(m, patterns); | |
217 | if (r < 0) | |
218 | return bus_log_create_error(r); | |
219 | ||
220 | r = sd_bus_call(bus, m, 0, &error, &reply); | |
221 | if (r < 0 && (sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_METHOD, | |
222 | SD_BUS_ERROR_ACCESS_DENIED))) { | |
223 | /* Fallback to legacy ListUnitsFiltered method */ | |
224 | fallback = true; | |
225 | log_debug_errno(r, "Failed to list units: %s Falling back to ListUnitsFiltered method.", bus_error_message(&error, r)); | |
226 | m = sd_bus_message_unref(m); | |
227 | sd_bus_error_free(&error); | |
228 | ||
229 | r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsFiltered"); | |
230 | if (r < 0) | |
231 | return bus_log_create_error(r); | |
232 | ||
233 | r = sd_bus_message_append_strv(m, arg_states); | |
234 | if (r < 0) | |
235 | return bus_log_create_error(r); | |
236 | ||
237 | r = sd_bus_call(bus, m, 0, &error, &reply); | |
238 | } | |
239 | if (r < 0) | |
240 | return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); | |
241 | ||
242 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); | |
243 | if (r < 0) | |
244 | return bus_log_parse_error(r); | |
245 | ||
246 | for (;;) { | |
247 | UnitInfo u; | |
248 | ||
249 | r = bus_parse_unit_info(reply, &u); | |
250 | if (r < 0) | |
251 | return bus_log_parse_error(r); | |
252 | if (r == 0) | |
253 | break; | |
254 | ||
255 | u.machine = machine; | |
256 | ||
257 | if (!output_show_unit(&u, fallback ? patterns : NULL)) | |
258 | continue; | |
259 | ||
319a4f4b | 260 | if (!GREEDY_REALLOC(*unit_infos, c+1)) |
daf71ef6 LP |
261 | return log_oom(); |
262 | ||
263 | (*unit_infos)[c++] = u; | |
264 | } | |
265 | ||
266 | r = sd_bus_message_exit_container(reply); | |
267 | if (r < 0) | |
268 | return bus_log_parse_error(r); | |
269 | ||
270 | *ret_reply = TAKE_PTR(reply); | |
271 | return c; | |
272 | } | |
273 | ||
f27d595d MY |
274 | int expand_unit_names( |
275 | sd_bus *bus, | |
276 | char * const *names, | |
277 | const char *suffix, | |
278 | char ***ret, | |
279 | bool *ret_expanded) { | |
280 | ||
daf71ef6 | 281 | _cleanup_strv_free_ char **mangled = NULL, **globs = NULL; |
deaf4b86 | 282 | int r; |
daf71ef6 LP |
283 | |
284 | assert(bus); | |
285 | assert(ret); | |
286 | ||
287 | STRV_FOREACH(name, names) { | |
288 | UnitNameMangle options = UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN); | |
289 | char *t; | |
290 | ||
291 | r = unit_name_mangle_with_suffix(*name, NULL, options, suffix ?: ".service", &t); | |
292 | if (r < 0) | |
293 | return log_error_errno(r, "Failed to mangle name: %m"); | |
294 | ||
295 | if (string_is_glob(t)) | |
296 | r = strv_consume(&globs, t); | |
297 | else | |
298 | r = strv_consume(&mangled, t); | |
299 | if (r < 0) | |
300 | return log_oom(); | |
301 | } | |
302 | ||
303 | /* Query the manager only if any of the names are a glob, since this is fairly expensive */ | |
304 | bool expanded = !strv_isempty(globs); | |
305 | if (expanded) { | |
306 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
307 | _cleanup_free_ UnitInfo *unit_infos = NULL; | |
daf71ef6 LP |
308 | |
309 | r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply); | |
310 | if (r < 0) | |
311 | return r; | |
312 | ||
f27d595d MY |
313 | FOREACH_ARRAY(info, unit_infos, r) |
314 | if (strv_extend(&mangled, info->id) < 0) | |
daf71ef6 | 315 | return log_oom(); |
daf71ef6 LP |
316 | } |
317 | ||
f27d595d | 318 | *ret = TAKE_PTR(mangled); |
daf71ef6 LP |
319 | if (ret_expanded) |
320 | *ret_expanded = expanded; | |
321 | ||
daf71ef6 LP |
322 | return 0; |
323 | } | |
324 | ||
002db03f | 325 | int get_active_triggering_units(sd_bus *bus, const char *unit, bool ignore_masked, char ***ret) { |
daf71ef6 | 326 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
002db03f MY |
327 | _cleanup_strv_free_ char **triggered_by = NULL, **active = NULL; |
328 | _cleanup_free_ char *name = NULL, *dbus_path = NULL; | |
daf71ef6 LP |
329 | int r; |
330 | ||
002db03f MY |
331 | assert(bus); |
332 | assert(unit); | |
333 | assert(ret); | |
daf71ef6 | 334 | |
002db03f | 335 | r = unit_name_mangle(unit, 0, &name); |
daf71ef6 LP |
336 | if (r < 0) |
337 | return r; | |
338 | ||
09d6038d MY |
339 | if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) |
340 | goto skip; | |
341 | ||
002db03f MY |
342 | if (ignore_masked) { |
343 | r = unit_is_masked(bus, name); | |
344 | if (r < 0) | |
345 | return r; | |
09d6038d MY |
346 | if (r > 0) |
347 | goto skip; | |
002db03f | 348 | } |
daf71ef6 | 349 | |
002db03f | 350 | dbus_path = unit_dbus_path_from_name(name); |
daf71ef6 | 351 | if (!dbus_path) |
002db03f | 352 | return -ENOMEM; |
daf71ef6 LP |
353 | |
354 | r = sd_bus_get_property_strv( | |
355 | bus, | |
356 | "org.freedesktop.systemd1", | |
357 | dbus_path, | |
358 | "org.freedesktop.systemd1.Unit", | |
359 | "TriggeredBy", | |
360 | &error, | |
361 | &triggered_by); | |
362 | if (r < 0) | |
002db03f MY |
363 | return log_debug_errno(r, "Failed to get TriggeredBy property of unit '%s': %s", |
364 | name, bus_error_message(&error, r)); | |
daf71ef6 LP |
365 | |
366 | STRV_FOREACH(i, triggered_by) { | |
c4da8e90 ZJS |
367 | UnitActiveState active_state; |
368 | ||
daf71ef6 LP |
369 | r = get_state_one_unit(bus, *i, &active_state); |
370 | if (r < 0) | |
371 | return r; | |
372 | ||
5162829e | 373 | if (!IN_SET(active_state, UNIT_ACTIVE, UNIT_RELOADING, UNIT_REFRESHING)) |
daf71ef6 LP |
374 | continue; |
375 | ||
002db03f MY |
376 | r = strv_extend(&active, *i); |
377 | if (r < 0) | |
378 | return r; | |
daf71ef6 LP |
379 | } |
380 | ||
002db03f | 381 | *ret = TAKE_PTR(active); |
daf71ef6 | 382 | return 0; |
09d6038d MY |
383 | |
384 | skip: | |
385 | *ret = NULL; | |
386 | return 0; | |
daf71ef6 LP |
387 | } |
388 | ||
002db03f MY |
389 | void warn_triggering_units(sd_bus *bus, const char *unit, const char *operation, bool ignore_masked) { |
390 | _cleanup_strv_free_ char **triggered_by = NULL; | |
391 | _cleanup_free_ char *joined = NULL; | |
392 | int r; | |
393 | ||
394 | assert(bus); | |
395 | assert(unit); | |
396 | assert(operation); | |
397 | ||
398 | r = get_active_triggering_units(bus, unit, ignore_masked, &triggered_by); | |
399 | if (r < 0) { | |
701bd9d0 MY |
400 | if (r != -ENOENT) /* A linked unit might have disappeared after disabling */ |
401 | log_warning_errno(r, "Failed to get triggering units for '%s', ignoring: %m", unit); | |
002db03f MY |
402 | return; |
403 | } | |
404 | ||
405 | if (strv_isempty(triggered_by)) | |
406 | return; | |
407 | ||
408 | joined = strv_join(triggered_by, ", "); | |
409 | if (!joined) | |
410 | return (void) log_oom(); | |
411 | ||
412 | log_warning("%s '%s', but its triggering units are still active:\n" | |
413 | "%s", | |
414 | operation, unit, joined); | |
415 | } | |
416 | ||
daf71ef6 LP |
417 | int need_daemon_reload(sd_bus *bus, const char *unit) { |
418 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
419 | const char *path; | |
420 | int b, r; | |
421 | ||
422 | /* We ignore all errors here, since this is used to show a | |
423 | * warning only */ | |
424 | ||
425 | /* We don't use unit_dbus_path_from_name() directly since we | |
426 | * don't want to load the unit if it isn't loaded. */ | |
427 | ||
428 | r = bus_call_method(bus, bus_systemd_mgr, "GetUnit", NULL, &reply, "s", unit); | |
429 | if (r < 0) | |
430 | return r; | |
431 | ||
432 | r = sd_bus_message_read(reply, "o", &path); | |
433 | if (r < 0) | |
434 | return r; | |
435 | ||
436 | r = sd_bus_get_property_trivial( | |
437 | bus, | |
438 | "org.freedesktop.systemd1", | |
439 | path, | |
440 | "org.freedesktop.systemd1.Unit", | |
441 | "NeedDaemonReload", | |
442 | NULL, | |
443 | 'b', &b); | |
444 | if (r < 0) | |
445 | return r; | |
446 | ||
447 | return b; | |
448 | } | |
449 | ||
450 | void warn_unit_file_changed(const char *unit) { | |
451 | assert(unit); | |
452 | ||
37257662 MY |
453 | if (arg_no_warn) |
454 | return; | |
455 | ||
55721877 | 456 | log_warning("Warning: The unit file, source configuration file or drop-ins of %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.", |
daf71ef6 | 457 | unit, |
4870133b | 458 | arg_runtime_scope == RUNTIME_SCOPE_SYSTEM ? "" : " --user"); |
daf71ef6 LP |
459 | } |
460 | ||
461 | int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **ret_unit_path) { | |
daf71ef6 LP |
462 | assert(lp); |
463 | assert(unit_name); | |
464 | ||
465 | STRV_FOREACH(p, lp->search_path) { | |
466 | _cleanup_free_ char *path = NULL, *lpath = NULL; | |
467 | int r; | |
468 | ||
469 | path = path_join(*p, unit_name); | |
470 | if (!path) | |
471 | return log_oom(); | |
472 | ||
f461a28d | 473 | r = chase(path, arg_root, 0, &lpath, NULL); |
daf71ef6 LP |
474 | if (r == -ENOENT) |
475 | continue; | |
476 | if (r == -ENOMEM) | |
477 | return log_oom(); | |
478 | if (r < 0) | |
479 | return log_error_errno(r, "Failed to access path \"%s\": %m", path); | |
480 | ||
481 | if (ret_unit_path) | |
482 | *ret_unit_path = TAKE_PTR(lpath); | |
483 | ||
484 | return 1; | |
485 | } | |
486 | ||
487 | if (ret_unit_path) | |
488 | *ret_unit_path = NULL; | |
489 | ||
490 | return 0; | |
491 | } | |
492 | ||
493 | int unit_find_paths( | |
494 | sd_bus *bus, | |
495 | const char *unit_name, | |
496 | LookupPaths *lp, | |
497 | bool force_client_side, | |
daf71ef6 | 498 | Hashmap **cached_id_map, |
08ba85c7 | 499 | Hashmap **cached_name_map, |
daf71ef6 LP |
500 | char **ret_fragment_path, |
501 | char ***ret_dropin_paths) { | |
502 | ||
503 | _cleanup_strv_free_ char **dropins = NULL; | |
504 | _cleanup_free_ char *path = NULL; | |
505 | int r; | |
506 | ||
507 | /** | |
508 | * Finds where the unit is defined on disk. Returns 0 if the unit is not found. Returns 1 if it is | |
509 | * found, and sets: | |
1842c1b2 | 510 | * |
daf71ef6 | 511 | * - the path to the unit in *ret_frament_path, if it exists on disk, |
1842c1b2 | 512 | * |
daf71ef6 LP |
513 | * - and a strv of existing drop-ins in *ret_dropin_paths, if the arg is not NULL and any dropins |
514 | * were found. | |
515 | * | |
516 | * Returns -ERFKILL if the unit is masked, and -EKEYREJECTED if the unit file could not be loaded for | |
1842c1b2 LP |
517 | * some reason (the latter only applies if we are going through the service manager). As special |
518 | * exception it won't log for these two error cases. | |
daf71ef6 LP |
519 | */ |
520 | ||
521 | assert(unit_name); | |
522 | assert(ret_fragment_path); | |
523 | assert(lp); | |
524 | ||
525 | /* Go via the bus to acquire the path, unless we are explicitly told not to, or when the unit name is a template */ | |
526 | if (!force_client_side && | |
527 | !install_client_side() && | |
528 | !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { | |
529 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
530 | _cleanup_free_ char *load_state = NULL, *dbus_path = NULL; | |
531 | ||
532 | dbus_path = unit_dbus_path_from_name(unit_name); | |
533 | if (!dbus_path) | |
534 | return log_oom(); | |
535 | ||
536 | r = sd_bus_get_property_string( | |
537 | bus, | |
538 | "org.freedesktop.systemd1", | |
539 | dbus_path, | |
540 | "org.freedesktop.systemd1.Unit", | |
541 | "LoadState", | |
542 | &error, | |
543 | &load_state); | |
544 | if (r < 0) | |
545 | return log_error_errno(r, "Failed to get LoadState: %s", bus_error_message(&error, r)); | |
546 | ||
547 | if (streq(load_state, "masked")) | |
1842c1b2 | 548 | return -ERFKILL; /* special case: no logging */ |
daf71ef6 LP |
549 | if (streq(load_state, "not-found")) { |
550 | r = 0; | |
d5427dd2 | 551 | goto finish; |
daf71ef6 LP |
552 | } |
553 | if (!STR_IN_SET(load_state, "loaded", "bad-setting")) | |
1842c1b2 | 554 | return -EKEYREJECTED; /* special case: no logging */ |
daf71ef6 LP |
555 | |
556 | r = sd_bus_get_property_string( | |
557 | bus, | |
558 | "org.freedesktop.systemd1", | |
559 | dbus_path, | |
560 | "org.freedesktop.systemd1.Unit", | |
561 | "FragmentPath", | |
562 | &error, | |
563 | &path); | |
564 | if (r < 0) | |
565 | return log_error_errno(r, "Failed to get FragmentPath: %s", bus_error_message(&error, r)); | |
566 | ||
567 | if (ret_dropin_paths) { | |
568 | r = sd_bus_get_property_strv( | |
569 | bus, | |
570 | "org.freedesktop.systemd1", | |
571 | dbus_path, | |
572 | "org.freedesktop.systemd1.Unit", | |
573 | "DropInPaths", | |
574 | &error, | |
575 | &dropins); | |
576 | if (r < 0) | |
577 | return log_error_errno(r, "Failed to get DropInPaths: %s", bus_error_message(&error, r)); | |
578 | } | |
579 | } else { | |
daf71ef6 LP |
580 | if (!*cached_name_map) { |
581 | r = unit_file_build_name_map(lp, NULL, cached_id_map, cached_name_map, NULL); | |
582 | if (r < 0) | |
583 | return r; | |
584 | } | |
585 | ||
5793a39d YW |
586 | const char *_path; |
587 | _cleanup_set_free_ Set *names = NULL; | |
daf71ef6 LP |
588 | r = unit_file_find_fragment(*cached_id_map, *cached_name_map, unit_name, &_path, &names); |
589 | if (r < 0) | |
b420e6f0 | 590 | return log_error_errno(r, "Failed to find fragment for '%s': %m", unit_name); |
daf71ef6 LP |
591 | |
592 | if (_path) { | |
593 | path = strdup(_path); | |
594 | if (!path) | |
595 | return log_oom(); | |
596 | } | |
597 | ||
598 | if (ret_dropin_paths) { | |
599 | r = unit_file_find_dropin_paths(arg_root, lp->search_path, NULL, | |
600 | ".d", ".conf", | |
601 | NULL, names, &dropins); | |
602 | if (r < 0) | |
603 | return r; | |
604 | } | |
605 | } | |
606 | ||
d5427dd2 | 607 | finish: |
daf71ef6 LP |
608 | if (isempty(path)) { |
609 | *ret_fragment_path = NULL; | |
610 | r = 0; | |
611 | } else { | |
612 | *ret_fragment_path = TAKE_PTR(path); | |
613 | r = 1; | |
614 | } | |
615 | ||
616 | if (ret_dropin_paths) { | |
617 | if (!strv_isempty(dropins)) { | |
618 | *ret_dropin_paths = TAKE_PTR(dropins); | |
619 | r = 1; | |
620 | } else | |
621 | *ret_dropin_paths = NULL; | |
622 | } | |
623 | ||
daf71ef6 LP |
624 | if (r == 0 && !arg_force) |
625 | log_error("No files found for %s.", unit_name); | |
626 | ||
627 | return r; | |
628 | } | |
629 | ||
630 | static int unit_find_template_path( | |
631 | const char *unit_name, | |
632 | LookupPaths *lp, | |
633 | char **ret_fragment_path, | |
634 | char **ret_template) { | |
635 | ||
636 | _cleanup_free_ char *t = NULL, *f = NULL; | |
637 | int r; | |
638 | ||
639 | /* Returns 1 if a fragment was found, 0 if not found, negative on error. */ | |
640 | ||
641 | r = unit_file_find_path(lp, unit_name, &f); | |
642 | if (r < 0) | |
643 | return r; | |
644 | if (r > 0) { | |
645 | if (ret_fragment_path) | |
646 | *ret_fragment_path = TAKE_PTR(f); | |
647 | if (ret_template) | |
648 | *ret_template = NULL; | |
649 | return r; /* found a real unit */ | |
650 | } | |
651 | ||
652 | r = unit_name_template(unit_name, &t); | |
653 | if (r == -EINVAL) { | |
654 | if (ret_fragment_path) | |
655 | *ret_fragment_path = NULL; | |
656 | if (ret_template) | |
657 | *ret_template = NULL; | |
658 | ||
659 | return 0; /* not a template, does not exist */ | |
660 | } | |
661 | if (r < 0) | |
662 | return log_error_errno(r, "Failed to determine template name: %m"); | |
663 | ||
664 | r = unit_file_find_path(lp, t, ret_fragment_path); | |
665 | if (r < 0) | |
666 | return r; | |
667 | ||
668 | if (ret_template) | |
669 | *ret_template = r > 0 ? TAKE_PTR(t) : NULL; | |
670 | ||
671 | return r; | |
672 | } | |
673 | ||
6ea32f61 | 674 | int unit_is_masked(sd_bus *bus, const char *unit) { |
daf71ef6 LP |
675 | _cleanup_free_ char *load_state = NULL; |
676 | int r; | |
677 | ||
6ea32f61 MY |
678 | assert(bus); |
679 | assert(unit); | |
daf71ef6 | 680 | |
6ea32f61 MY |
681 | if (unit_name_is_valid(unit, UNIT_NAME_TEMPLATE)) { |
682 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
683 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
684 | const char *state; | |
daf71ef6 | 685 | |
6ea32f61 | 686 | r = bus_call_method(bus, bus_systemd_mgr, "GetUnitFileState", &error, &reply, "s", unit); |
daf71ef6 | 687 | if (r < 0) |
6ea32f61 MY |
688 | return log_debug_errno(r, "Failed to get UnitFileState for '%s': %s", |
689 | unit, bus_error_message(&error, r)); | |
690 | ||
691 | r = sd_bus_message_read(reply, "s", &state); | |
692 | if (r < 0) | |
693 | return bus_log_parse_error_debug(r); | |
694 | ||
695 | return STR_IN_SET(state, "masked", "masked-runtime"); | |
daf71ef6 LP |
696 | } |
697 | ||
6ea32f61 | 698 | r = unit_load_state(bus, unit, &load_state); |
daf71ef6 LP |
699 | if (r < 0) |
700 | return r; | |
701 | ||
702 | return streq(load_state, "masked"); | |
703 | } | |
704 | ||
705 | int unit_exists(LookupPaths *lp, const char *unit) { | |
706 | typedef struct UnitStateInfo { | |
707 | const char *load_state; | |
708 | const char *active_state; | |
709 | } UnitStateInfo; | |
710 | ||
711 | static const struct bus_properties_map property_map[] = { | |
712 | { "LoadState", "s", NULL, offsetof(UnitStateInfo, load_state) }, | |
713 | { "ActiveState", "s", NULL, offsetof(UnitStateInfo, active_state) }, | |
714 | {}, | |
715 | }; | |
716 | ||
717 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
718 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; | |
719 | _cleanup_free_ char *path = NULL; | |
720 | UnitStateInfo info = {}; | |
721 | sd_bus *bus; | |
722 | int r; | |
723 | ||
724 | if (unit_name_is_valid(unit, UNIT_NAME_TEMPLATE)) | |
725 | return unit_find_template_path(unit, lp, NULL, NULL); | |
726 | ||
727 | path = unit_dbus_path_from_name(unit); | |
728 | if (!path) | |
729 | return log_oom(); | |
730 | ||
731 | r = acquire_bus(BUS_MANAGER, &bus); | |
732 | if (r < 0) | |
733 | return r; | |
734 | ||
735 | r = bus_map_all_properties(bus, "org.freedesktop.systemd1", path, property_map, 0, &error, &m, &info); | |
736 | if (r < 0) | |
737 | return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); | |
738 | ||
739 | return !streq_ptr(info.load_state, "not-found") || !streq_ptr(info.active_state, "inactive"); | |
740 | } | |
741 | ||
daf71ef6 LP |
742 | int append_unit_dependencies(sd_bus *bus, char **names, char ***ret) { |
743 | _cleanup_strv_free_ char **with_deps = NULL; | |
daf71ef6 LP |
744 | |
745 | assert(bus); | |
746 | assert(ret); | |
747 | ||
748 | STRV_FOREACH(name, names) { | |
a2c8652a | 749 | char **deps; |
daf71ef6 LP |
750 | |
751 | if (strv_extend(&with_deps, *name) < 0) | |
752 | return log_oom(); | |
753 | ||
754 | (void) unit_get_dependencies(bus, *name, &deps); | |
755 | ||
a2c8652a | 756 | if (strv_extend_strv_consume(&with_deps, deps, /* filter_duplicates = */ true) < 0) |
daf71ef6 LP |
757 | return log_oom(); |
758 | } | |
759 | ||
760 | *ret = TAKE_PTR(with_deps); | |
761 | ||
762 | return 0; | |
763 | } | |
764 | ||
765 | int maybe_extend_with_unit_dependencies(sd_bus *bus, char ***list) { | |
766 | _cleanup_strv_free_ char **list_with_deps = NULL; | |
767 | int r; | |
768 | ||
769 | assert(bus); | |
770 | assert(list); | |
771 | ||
772 | if (!arg_with_dependencies) | |
773 | return 0; | |
774 | ||
775 | r = append_unit_dependencies(bus, *list, &list_with_deps); | |
776 | if (r < 0) | |
777 | return log_error_errno(r, "Failed to append unit dependencies: %m"); | |
778 | ||
d9219935 | 779 | return strv_free_and_replace(*list, list_with_deps); |
daf71ef6 LP |
780 | } |
781 | ||
782 | int unit_get_dependencies(sd_bus *bus, const char *name, char ***ret) { | |
783 | _cleanup_strv_free_ char **deps = NULL; | |
784 | ||
cbc2593e | 785 | static const struct bus_properties_map map[_DEPENDENCY_MAX][7] = { |
daf71ef6 LP |
786 | [DEPENDENCY_FORWARD] = { |
787 | { "Requires", "as", NULL, 0 }, | |
788 | { "Requisite", "as", NULL, 0 }, | |
789 | { "Wants", "as", NULL, 0 }, | |
790 | { "ConsistsOf", "as", NULL, 0 }, | |
791 | { "BindsTo", "as", NULL, 0 }, | |
cbc2593e | 792 | { "Upholds", "as", NULL, 0 }, |
daf71ef6 LP |
793 | {} |
794 | }, | |
795 | [DEPENDENCY_REVERSE] = { | |
796 | { "RequiredBy", "as", NULL, 0 }, | |
797 | { "RequisiteOf", "as", NULL, 0 }, | |
798 | { "WantedBy", "as", NULL, 0 }, | |
799 | { "PartOf", "as", NULL, 0 }, | |
800 | { "BoundBy", "as", NULL, 0 }, | |
cbc2593e | 801 | { "UpheldBy", "as", NULL, 0 }, |
daf71ef6 LP |
802 | {} |
803 | }, | |
804 | [DEPENDENCY_AFTER] = { | |
805 | { "After", "as", NULL, 0 }, | |
806 | {} | |
807 | }, | |
808 | [DEPENDENCY_BEFORE] = { | |
809 | { "Before", "as", NULL, 0 }, | |
810 | {} | |
811 | }, | |
812 | }; | |
813 | ||
814 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
815 | _cleanup_free_ char *dbus_path = NULL; | |
816 | int r; | |
817 | ||
818 | assert(bus); | |
819 | assert(name); | |
820 | assert(ret); | |
821 | ||
822 | dbus_path = unit_dbus_path_from_name(name); | |
823 | if (!dbus_path) | |
824 | return log_oom(); | |
825 | ||
826 | r = bus_map_all_properties(bus, | |
827 | "org.freedesktop.systemd1", | |
828 | dbus_path, | |
829 | map[arg_dependency], | |
830 | BUS_MAP_STRDUP, | |
831 | &error, | |
832 | NULL, | |
833 | &deps); | |
834 | if (r < 0) | |
835 | return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r)); | |
836 | ||
837 | strv_uniq(deps); /* Sometimes a unit might have multiple deps on the other unit, | |
838 | * but we still want to show it just once. */ | |
839 | *ret = TAKE_PTR(deps); | |
840 | ||
841 | return 0; | |
842 | } | |
843 | ||
844 | const char* unit_type_suffix(const char *unit) { | |
845 | const char *dot; | |
846 | ||
847 | dot = strrchr(unit, '.'); | |
848 | if (!dot) | |
849 | return ""; | |
850 | ||
851 | return dot + 1; | |
852 | } | |
853 | ||
854 | bool output_show_unit(const UnitInfo *u, char **patterns) { | |
855 | assert(u); | |
856 | ||
857 | if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) | |
858 | return false; | |
859 | ||
d29cc4d6 | 860 | if (arg_types && !strv_contains(arg_types, unit_type_suffix(u->id))) |
daf71ef6 LP |
861 | return false; |
862 | ||
863 | if (arg_all) | |
864 | return true; | |
865 | ||
866 | /* Note that '--all' is not purely a state filter, but also a filter that hides units that "follow" | |
867 | * other units (which is used for device units that appear under different names). */ | |
868 | if (!isempty(u->following)) | |
869 | return false; | |
870 | ||
871 | if (!strv_isempty(arg_states)) | |
872 | return true; | |
873 | ||
874 | /* By default show all units except the ones in inactive state and with no pending job */ | |
875 | if (u->job_id > 0) | |
876 | return true; | |
877 | ||
878 | if (streq(u->active_state, "inactive")) | |
879 | return false; | |
880 | ||
881 | return true; | |
882 | } | |
883 | ||
884 | bool install_client_side(void) { | |
885 | /* Decides when to execute enable/disable/... operations client-side rather than server-side. */ | |
886 | ||
887 | if (running_in_chroot_or_offline()) | |
888 | return true; | |
889 | ||
890 | if (sd_booted() <= 0) | |
891 | return true; | |
892 | ||
893 | if (!isempty(arg_root)) | |
894 | return true; | |
895 | ||
4870133b | 896 | if (arg_runtime_scope == RUNTIME_SCOPE_GLOBAL) |
daf71ef6 LP |
897 | return true; |
898 | ||
899 | /* Unsupported environment variable, mostly for debugging purposes */ | |
900 | if (getenv_bool("SYSTEMCTL_INSTALL_CLIENT_SIDE") > 0) | |
901 | return true; | |
902 | ||
903 | return false; | |
904 | } | |
905 | ||
906 | int output_table(Table *table) { | |
907 | int r; | |
908 | ||
909 | assert(table); | |
910 | ||
911 | if (OUTPUT_MODE_IS_JSON(arg_output)) | |
309a747f | 912 | r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | SD_JSON_FORMAT_COLOR_AUTO); |
daf71ef6 LP |
913 | else |
914 | r = table_print(table, NULL); | |
915 | if (r < 0) | |
916 | return table_log_print_error(r); | |
917 | ||
918 | return 0; | |
919 | } | |
920 | ||
921 | bool show_preset_for_state(UnitFileState state) { | |
922 | /* Don't show preset state in those unit file states, it'll only confuse users. */ | |
923 | return !IN_SET(state, | |
924 | UNIT_FILE_ALIAS, | |
925 | UNIT_FILE_STATIC, | |
926 | UNIT_FILE_GENERATED, | |
927 | UNIT_FILE_TRANSIENT); | |
928 | } | |
929 | ||
930 | UnitFileFlags unit_file_flags_from_args(void) { | |
931 | return (arg_runtime ? UNIT_FILE_RUNTIME : 0) | | |
932 | (arg_force ? UNIT_FILE_FORCE : 0); | |
933 | } | |
934 | ||
f27d595d | 935 | int mangle_names(const char *operation, char * const *original_names, char ***ret) { |
daf71ef6 | 936 | _cleanup_strv_free_ char **l = NULL; |
daf71ef6 LP |
937 | int r; |
938 | ||
f27d595d MY |
939 | assert(operation); |
940 | assert(ret); | |
daf71ef6 LP |
941 | |
942 | STRV_FOREACH(name, original_names) { | |
f27d595d | 943 | char *mangled; |
daf71ef6 | 944 | |
23484e12 | 945 | if (is_path(*name)) |
f27d595d MY |
946 | /* When enabling units qualified path names are OK, too, hence allow them explicitly. */ |
947 | r = path_make_absolute_cwd(*name, &mangled); | |
23484e12 | 948 | else |
daf71ef6 LP |
949 | r = unit_name_mangle_with_suffix(*name, operation, |
950 | arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, | |
f27d595d MY |
951 | ".service", &mangled); |
952 | if (r < 0) | |
23484e12 | 953 | return log_error_errno(r, "Failed to mangle unit name or path '%s': %m", *name); |
daf71ef6 | 954 | |
f27d595d MY |
955 | if (strv_consume(&l, mangled) < 0) |
956 | return log_oom(); | |
daf71ef6 LP |
957 | } |
958 | ||
f27d595d | 959 | *ret = TAKE_PTR(l); |
daf71ef6 LP |
960 | |
961 | return 0; | |
962 | } | |
963 | ||
964 | int halt_now(enum action a) { | |
965 | /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need | |
966 | * to be synced explicitly in advance. */ | |
967 | if (!arg_no_sync && !arg_dry_run) | |
b7106af4 | 968 | sync(); |
daf71ef6 LP |
969 | |
970 | /* Make sure C-A-D is handled by the kernel from this point on... */ | |
971 | if (!arg_dry_run) | |
972 | (void) reboot(RB_ENABLE_CAD); | |
973 | ||
974 | switch (a) { | |
975 | ||
976 | case ACTION_HALT: | |
977 | if (!arg_quiet) | |
978 | log_info("Halting."); | |
979 | if (arg_dry_run) | |
980 | return 0; | |
981 | (void) reboot(RB_HALT_SYSTEM); | |
982 | return -errno; | |
983 | ||
984 | case ACTION_POWEROFF: | |
985 | if (!arg_quiet) | |
986 | log_info("Powering off."); | |
987 | if (arg_dry_run) | |
988 | return 0; | |
989 | (void) reboot(RB_POWER_OFF); | |
990 | return -errno; | |
991 | ||
992 | case ACTION_KEXEC: | |
993 | case ACTION_REBOOT: | |
994 | return reboot_with_parameter(REBOOT_FALLBACK | | |
995 | (arg_quiet ? 0 : REBOOT_LOG) | | |
996 | (arg_dry_run ? REBOOT_DRY_RUN : 0)); | |
997 | ||
998 | default: | |
04499a70 | 999 | assert_not_reached(); |
daf71ef6 LP |
1000 | } |
1001 | } | |
716a4cdb MY |
1002 | |
1003 | int get_unit_by_pid(sd_bus *bus, pid_t pid, char **ret_unit, char **ret_path) { | |
1004 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
1005 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
1006 | int r; | |
1007 | ||
1008 | assert(bus); | |
1009 | assert(pid >= 0); /* 0 is accepted by GetUnitByPID for querying our own process. */ | |
1010 | ||
1011 | r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", (uint32_t) pid); | |
1012 | if (r < 0) { | |
1013 | if (sd_bus_error_has_name(&error, BUS_ERROR_NO_UNIT_FOR_PID)) | |
1014 | return log_error_errno(r, "%s", bus_error_message(&error, r)); | |
1015 | ||
1016 | return log_error_errno(r, | |
1017 | "Failed to get unit that PID " PID_FMT " belongs to: %s", | |
1018 | pid > 0 ? pid : getpid_cached(), | |
1019 | bus_error_message(&error, r)); | |
1020 | } | |
1021 | ||
1022 | _cleanup_free_ char *u = NULL, *p = NULL; | |
1023 | const char *path; | |
1024 | ||
1025 | r = sd_bus_message_read_basic(reply, 'o', &path); | |
1026 | if (r < 0) | |
1027 | return bus_log_parse_error(r); | |
1028 | ||
1029 | if (ret_unit) { | |
1030 | r = unit_name_from_dbus_path(path, &u); | |
1031 | if (r < 0) | |
1032 | return log_error_errno(r, | |
1033 | "Failed to extract unit name from D-Bus object path '%s': %m", | |
1034 | path); | |
1035 | } | |
1036 | ||
1037 | if (ret_path) { | |
1038 | p = strdup(path); | |
1039 | if (!p) | |
1040 | return log_oom(); | |
1041 | } | |
1042 | ||
1043 | if (ret_unit) | |
1044 | *ret_unit = TAKE_PTR(u); | |
1045 | if (ret_path) | |
1046 | *ret_path = TAKE_PTR(p); | |
1047 | ||
1048 | return 0; | |
1049 | } | |
1050 | ||
1051 | static int get_unit_by_pidfd(sd_bus *bus, const PidRef *pid, char **ret_unit, char **ret_path) { | |
1052 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
1053 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
1054 | int r; | |
1055 | ||
1056 | assert(bus); | |
1057 | assert(pidref_is_set(pid)); | |
1058 | ||
1059 | if (pid->fd < 0) | |
1060 | return -EOPNOTSUPP; | |
1061 | ||
1062 | r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPIDFD", &error, &reply, "h", pid->fd); | |
1063 | if (r < 0) { | |
1064 | if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) | |
1065 | return -EOPNOTSUPP; | |
1066 | ||
1067 | if (sd_bus_error_has_names(&error, BUS_ERROR_NO_UNIT_FOR_PID, BUS_ERROR_NO_SUCH_PROCESS)) | |
1068 | return log_error_errno(r, "%s", bus_error_message(&error, r)); | |
1069 | ||
1070 | return log_error_errno(r, | |
1071 | "Failed to get unit that PID " PID_FMT " belongs to: %s", | |
1072 | pid->pid, bus_error_message(&error, r)); | |
1073 | } | |
1074 | ||
1075 | _cleanup_free_ char *u = NULL, *p = NULL; | |
1076 | const char *path, *unit; | |
1077 | ||
1078 | r = sd_bus_message_read(reply, "os", &path, &unit); | |
1079 | if (r < 0) | |
1080 | return bus_log_parse_error(r); | |
1081 | ||
1082 | if (ret_unit) { | |
1083 | u = strdup(unit); | |
1084 | if (!u) | |
1085 | return log_oom(); | |
1086 | } | |
1087 | ||
1088 | if (ret_path) { | |
1089 | p = strdup(path); | |
1090 | if (!p) | |
1091 | return log_oom(); | |
1092 | } | |
1093 | ||
1094 | if (ret_unit) | |
1095 | *ret_unit = TAKE_PTR(u); | |
1096 | if (ret_path) | |
1097 | *ret_path = TAKE_PTR(p); | |
1098 | ||
1099 | return 0; | |
1100 | } | |
1101 | ||
1102 | int lookup_unit_by_pidref(sd_bus *bus, pid_t pid, char **ret_unit, char **ret_path) { | |
1103 | int r; | |
1104 | ||
1105 | assert(bus); | |
1106 | assert(pid >= 0); /* 0 means our own process */ | |
1107 | ||
1108 | if (arg_transport != BUS_TRANSPORT_LOCAL) | |
1109 | return get_unit_by_pid(bus, pid, ret_unit, ret_path); | |
1110 | ||
1111 | static bool use_pidfd = true; | |
1112 | _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; | |
1113 | ||
1114 | r = pidref_set_pid(&pidref, pid); | |
1115 | if (r < 0) | |
1116 | return log_error_errno(r, | |
1117 | r == -ESRCH ? | |
1118 | "PID " PID_FMT " doesn't exist or is already gone." : | |
1119 | "Failed to create reference to PID " PID_FMT ": %m", | |
1120 | pid); | |
1121 | ||
1122 | if (use_pidfd) { | |
1123 | r = get_unit_by_pidfd(bus, &pidref, ret_unit, ret_path); | |
1124 | if (r != -EOPNOTSUPP) | |
1125 | return r; | |
1126 | ||
1127 | use_pidfd = false; | |
1128 | log_debug_errno(r, "Unable to look up process using pidfd, falling back to pid."); | |
1129 | } | |
1130 | ||
1131 | _cleanup_free_ char *u = NULL, *p = NULL; | |
1132 | ||
1133 | r = get_unit_by_pid(bus, pidref.pid, ret_unit ? &u : NULL, ret_path ? &p : NULL); | |
1134 | if (r < 0) | |
1135 | return r; | |
1136 | ||
1137 | r = pidref_verify(&pidref); | |
1138 | if (r < 0) | |
1139 | return log_error_errno(r, "Failed to verify our reference to PID " PID_FMT ": %m", pidref.pid); | |
1140 | ||
1141 | if (ret_unit) | |
1142 | *ret_unit = TAKE_PTR(u); | |
1143 | if (ret_path) | |
1144 | *ret_path = TAKE_PTR(p); | |
1145 | ||
1146 | return 0; | |
1147 | } |