diff options
Diffstat (limited to 'src/core/dbus-manager.c')
-rw-r--r-- | src/core/dbus-manager.c | 407 |
1 files changed, 265 insertions, 142 deletions
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 745f5cc..2515f54 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -11,6 +11,7 @@ #include "bus-common-errors.h" #include "bus-get-properties.h" #include "bus-log-control-api.h" +#include "bus-util.h" #include "chase.h" #include "confidential-virt.h" #include "data-fd-util.h" @@ -39,6 +40,7 @@ #include "string-util.h" #include "strv.h" #include "syslog-util.h" +#include "taint.h" #include "user-util.h" #include "version.h" #include "virt.h" @@ -125,13 +127,10 @@ static int property_get_tainted( void *userdata, sd_bus_error *error) { - _cleanup_free_ char *s = NULL; - Manager *m = ASSERT_PTR(userdata); - assert(bus); assert(reply); - s = manager_taint_string(m); + _cleanup_free_ char *s = taint_string(); if (!s) return log_oom(); @@ -464,18 +463,13 @@ static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char * its sleeve: if the name is specified empty we use the client's unit. */ if (isempty(name)) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - pid_t pid; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; - r = sd_bus_creds_get_pid(creds, &pid); + r = bus_query_sender_pidref(message, &pidref); if (r < 0) return r; - u = manager_get_unit_by_pid(m, pid); + u = manager_get_unit_by_pidref(m, &pidref); if (!u) return sd_bus_error_set(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit."); } else { @@ -542,7 +536,7 @@ static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = ASSERT_PTR(userdata); - pid_t pid; + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; Unit *u; int r; @@ -552,27 +546,20 @@ static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bu /* Anyone can call this method */ - r = sd_bus_message_read(message, "u", &pid); + r = sd_bus_message_read(message, "u", &pidref.pid); if (r < 0) return r; - if (pid < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PID " PID_FMT, pid); - - if (pid == 0) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); + if (pidref.pid < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PID " PID_FMT, pidref.pid); + if (pidref.pid == 0) { + r = bus_query_sender_pidref(message, &pidref); if (r < 0) return r; } - u = manager_get_unit_by_pid(m, pid); + u = manager_get_unit_by_pidref(m, &pidref); if (!u) - return sd_bus_error_setf(error, BUS_ERROR_NO_UNIT_FOR_PID, "PID "PID_FMT" does not belong to any loaded unit.", pid); + return sd_bus_error_setf(error, BUS_ERROR_NO_UNIT_FOR_PID, "PID "PID_FMT" does not belong to any loaded unit.", pidref.pid); return reply_unit_path(u, message, error); } @@ -581,41 +568,27 @@ static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userd _cleanup_free_ char *path = NULL; Manager *m = ASSERT_PTR(userdata); sd_id128_t id; - const void *a; Unit *u; - size_t sz; int r; assert(message); /* Anyone can call this method */ - r = sd_bus_message_read_array(message, 'y', &a, &sz); - if (r < 0) - return r; - if (sz == 0) - id = SD_ID128_NULL; - else if (sz == 16) - memcpy(&id, a, sz); - else + if (bus_message_read_id128(message, &id) < 0) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid invocation ID"); if (sd_id128_is_null(id)) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - pid_t pid; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; - r = sd_bus_creds_get_pid(creds, &pid); + r = bus_query_sender_pidref(message, &pidref); if (r < 0) return r; - u = manager_get_unit_by_pid(m, pid); + u = manager_get_unit_by_pidref(m, &pidref); if (!u) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, - "Client " PID_FMT " not member of any unit.", pid); + "Client " PID_FMT " not member of any unit.", pidref.pid); } else { u = hashmap_get(m->units_by_invocation_id, &id); if (!u) @@ -797,6 +770,7 @@ static int method_generic_unit_operation( assert(message); assert(m); + assert(handler); /* Read the first argument from the command and pass the operation to the specified per-unit * method. */ @@ -860,11 +834,13 @@ static int method_clean_unit(sd_bus_message *message, void *userdata, sd_bus_err } static int method_freeze_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_generic_unit_operation(message, userdata, error, bus_unit_method_freeze, 0); + /* Only active units can be frozen, which must be properly loaded already */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_freeze, GENERIC_UNIT_VALIDATE_LOADED); } static int method_thaw_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_generic_unit_operation(message, userdata, error, bus_unit_method_thaw, 0); + /* Same as freeze above */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_thaw, GENERIC_UNIT_VALIDATE_LOADED); } static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -972,9 +948,10 @@ static int method_list_units_by_names(sd_bus_message *message, void *userdata, s } static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { - /* Don't load a unit (since it won't have any processes if it's not loaded), but don't insist on the - * unit being loaded (because even improperly loaded units might still have processes around */ - return method_generic_unit_operation(message, userdata, error, bus_unit_method_get_processes, 0); + /* Don't load a unit actively (since it won't have any processes if it's not loaded), but don't + * insist on the unit being loaded either (because even improperly loaded units might still have + * processes around). */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_get_processes, /* flags = */ 0); } static int method_attach_processes_to_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1430,11 +1407,11 @@ static int dump_impl( * operations, and can cause PID1 to stall. So it seems similar enough in terms of security * considerations and impact, and thus use the same access check for dumps which, given the * large amount of data to fetch, can stall PID1 for quite some time. */ - r = mac_selinux_access_check(message, "reload", error); + r = mac_selinux_access_check(message, "reload", /* error = */ NULL); if (r < 0) goto ratelimited; - r = bus_verify_bypass_dump_ratelimit_async(m, message, error); + r = bus_verify_bypass_dump_ratelimit_async(m, message, /* error = */ NULL); if (r < 0) goto ratelimited; if (r == 0) @@ -1469,7 +1446,7 @@ static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *er static int reply_dump_by_fd(sd_bus_message *message, char *dump) { _cleanup_close_ int fd = -EBADF; - fd = acquire_data_fd(dump, strlen(dump), 0); + fd = acquire_data_fd(dump); if (fd < 0) return fd; @@ -1621,10 +1598,10 @@ static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error * return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ /* Write a log message noting the unit or process who requested the Reload() */ - log_caller(message, m, "Reloading"); + log_caller(message, m, "Reload"); /* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */ - if (!ratelimit_below(&m->reload_ratelimit)) { + if (!ratelimit_below(&m->reload_reexec_ratelimit)) { log_warning("Reloading request rejected due to rate limit."); return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, @@ -1667,7 +1644,15 @@ static int method_reexecute(sd_bus_message *message, void *userdata, sd_bus_erro return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ /* Write a log message noting the unit or process who requested the Reexecute() */ - log_caller(message, m, "Reexecuting"); + log_caller(message, m, "Reexecution"); + + /* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */ + if (!ratelimit_below(&m->reload_reexec_ratelimit)) { + log_warning("Reexecution request rejected due to rate limit."); + return sd_bus_error_setf(error, + SD_BUS_ERROR_LIMITS_EXCEEDED, + "Reexecute() request rejected due to rate limit."); + } /* We don't send a reply back here, the client should * just wait for us disconnecting. */ @@ -2329,85 +2314,53 @@ static int send_unit_files_changed(sd_bus *bus, void *userdata) { return sd_bus_send(bus, message, NULL); } -/* Create an error reply, using the error information from changes[] - * if possible, and fall back to generating an error from error code c. - * The error message only describes the first error. - */ +static void manager_unit_files_changed(Manager *m, const InstallChange *changes, size_t n_changes) { + int r; + + assert(m); + assert(changes || n_changes == 0); + + if (!install_changes_have_modification(changes, n_changes)) + return; + + /* See comments for this variable in manager.h */ + m->unit_file_state_outdated = true; + + r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL); + if (r < 0) + log_debug_errno(r, "Failed to send UnitFilesChanged signal, ignoring: %m"); +} + static int install_error( sd_bus_error *error, int c, InstallChange *changes, size_t n_changes) { - CLEANUP_ARRAY(changes, n_changes, install_changes_free); + int r; - for (size_t i = 0; i < n_changes; i++) + /* Create an error reply, using the error information from changes[] if possible, and fall back to + * generating an error from error code c. The error message only describes the first error. */ - /* When making changes here, make sure to also change install_changes_dump() in install.c. */ + assert(changes || n_changes == 0); - switch (changes[i].type) { - case 0 ... _INSTALL_CHANGE_TYPE_MAX: /* not errors */ - break; + CLEANUP_ARRAY(changes, n_changes, install_changes_free); - case -EEXIST: - if (changes[i].source) - return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, - "File %s already exists and is a symlink to %s.", - changes[i].path, changes[i].source); - return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, - "File %s already exists.", - changes[i].path); - - case -ERFKILL: - return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, - "Unit file %s is masked.", changes[i].path); - - case -EADDRNOTAVAIL: - return sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, - "Unit %s is transient or generated.", changes[i].path); - - case -ETXTBSY: - return sd_bus_error_setf(error, BUS_ERROR_UNIT_BAD_PATH, - "File %s is under the systemd unit hierarchy already.", changes[i].path); - - case -EBADSLT: - return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, - "Invalid specifier in %s.", changes[i].path); - - case -EIDRM: - return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, - "Destination unit %s is a non-template unit.", changes[i].path); - - case -EUCLEAN: - return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, - "\"%s\" is not a valid unit name.", - changes[i].path); - - case -ELOOP: - return sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED, - "Refusing to operate on alias name or linked unit file: %s", - changes[i].path); - - case -EXDEV: - if (changes[i].source) - return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, - "Cannot alias %s as %s.", - changes[i].source, changes[i].path); - return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, - "Invalid unit reference %s.", changes[i].path); - - case -ENOENT: - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, - "Unit file %s does not exist.", changes[i].path); + FOREACH_ARRAY(i, changes, n_changes) { + _cleanup_free_ char *err_message = NULL; + const char *bus_error; - case -EUNATCH: - return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, - "Cannot resolve specifiers in %s.", changes[i].path); + if (i->type >= 0) + continue; - default: - assert(changes[i].type < 0); /* other errors */ - return sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path); - } + r = install_change_dump_error(i, &err_message, &bus_error); + if (r == -ENOMEM) + return r; + if (r < 0) + return sd_bus_error_set_errnof(error, r, "File %s: %m", i->path); + + return sd_bus_error_set(error, bus_error, err_message); + } return c < 0 ? c : -EINVAL; } @@ -2426,12 +2379,6 @@ static int reply_install_changes_and_free( CLEANUP_ARRAY(changes, n_changes, install_changes_free); - if (install_changes_have_modification(changes, n_changes)) { - r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL); - if (r < 0) - log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m"); - } - r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; @@ -2446,18 +2393,17 @@ static int reply_install_changes_and_free( if (r < 0) return r; - for (size_t i = 0; i < n_changes; i++) { - - if (changes[i].type < 0) { + FOREACH_ARRAY(i, changes, n_changes) { + if (i->type < 0) { bad = true; continue; } r = sd_bus_message_append( reply, "(sss)", - install_change_type_to_string(changes[i].type), - changes[i].path, - changes[i].source); + install_change_type_to_string(i->type), + i->path, + i->source); if (r < 0) return r; @@ -2521,7 +2467,7 @@ static int method_enable_unit_files_generic( return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ r = call(m->runtime_scope, flags, NULL, l, &changes, &n_changes); - m->unit_file_state_outdated = m->unit_file_state_outdated || n_changes > 0; /* See comments for this variable in manager.h */ + manager_unit_files_changed(m, changes, n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2594,7 +2540,7 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ r = unit_file_preset(m->runtime_scope, flags, NULL, l, preset_mode, &changes, &n_changes); - m->unit_file_state_outdated = m->unit_file_state_outdated || n_changes > 0; /* See comments for this variable in manager.h */ + manager_unit_files_changed(m, changes, n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2648,7 +2594,7 @@ static int method_disable_unit_files_generic( return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ r = call(m->runtime_scope, flags, NULL, l, &changes, &n_changes); - m->unit_file_state_outdated = m->unit_file_state_outdated || n_changes > 0; /* See comments for this variable in manager.h */ + manager_unit_files_changed(m, changes, n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2691,7 +2637,7 @@ static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ r = unit_file_revert(m->runtime_scope, NULL, l, &changes, &n_changes); - m->unit_file_state_outdated = m->unit_file_state_outdated || n_changes > 0; /* See comments for this variable in manager.h */ + manager_unit_files_changed(m, changes, n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2722,6 +2668,7 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ r = unit_file_set_default(m->runtime_scope, force ? UNIT_FILE_FORCE : 0, NULL, name, &changes, &n_changes); + manager_unit_files_changed(m, changes, n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2764,7 +2711,7 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ r = unit_file_preset_all(m->runtime_scope, flags, NULL, preset_mode, &changes, &n_changes); - m->unit_file_state_outdated = m->unit_file_state_outdated || n_changes > 0; /* See comments for this variable in manager.h */ + manager_unit_files_changed(m, changes, n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2804,7 +2751,7 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd return -EINVAL; r = unit_file_add_dependency(m->runtime_scope, flags, NULL, l, target, dep, &changes, &n_changes); - m->unit_file_state_outdated = m->unit_file_state_outdated || n_changes > 0; /* See comments for this variable in manager.h */ + manager_unit_files_changed(m, changes, n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2933,6 +2880,175 @@ static int method_dump_unit_descriptor_store(sd_bus_message *message, void *user return method_generic_unit_operation(message, userdata, error, bus_service_method_dump_file_descriptor_store, 0); } +static int aux_scope_from_message(Manager *m, sd_bus_message *message, Unit **ret_scope, sd_bus_error *error) { + _cleanup_(pidref_done) PidRef sender_pidref = PIDREF_NULL; + _cleanup_free_ PidRef *pidrefs = NULL; + const char *name; + Unit *from, *scope; + PidRef *main_pid; + CGroupContext *cc; + size_t n_pids = 0; + uint64_t flags; + int r; + + assert(ret_scope); + + r = bus_query_sender_pidref(message, &sender_pidref); + if (r < 0) + return r; + + from = manager_get_unit_by_pidref(m, &sender_pidref); + if (!from) + return sd_bus_error_set(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit."); + + if (!IN_SET(from->type, UNIT_SERVICE, UNIT_SCOPE)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Starting auxiliary scope is supported only for service and scope units, refusing."); + + if (!unit_name_is_valid(from->id, UNIT_NAME_PLAIN)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Auxiliary scope can be started only for non-template service units and scope units, refusing."); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid name \"%s\" for auxiliary scope.", name); + + if (unit_name_to_type(name) != UNIT_SCOPE) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Name \"%s\" of auxiliary scope doesn't have .scope suffix.", name); + + main_pid = unit_main_pid(from); + + r = sd_bus_message_enter_container(message, 'a', "h"); + if (r < 0) + return r; + + for (;;) { + _cleanup_(pidref_done) PidRef p = PIDREF_NULL; + Unit *unit; + int fd; + + r = sd_bus_message_read(message, "h", &fd); + if (r < 0) + return r; + if (r == 0) + break; + + r = pidref_set_pidfd(&p, fd); + if (r < 0) { + log_unit_warning_errno(from, r, "Failed to create process reference from PIDFD, ignoring: %m"); + continue; + } + + unit = manager_get_unit_by_pidref(m, &p); + if (!unit) { + log_unit_warning(from, "Failed to get unit from PIDFD, ignoring."); + continue; + } + + if (!streq(unit->id, from->id)) { + log_unit_warning(from, "PID " PID_FMT " is not running in the same service as the calling process, ignoring.", p.pid); + continue; + } + + if (pidref_equal(main_pid, &p)) { + log_unit_warning(from, "Main PID cannot be migrated into auxiliary scope, ignoring."); + continue; + } + + if (!GREEDY_REALLOC(pidrefs, n_pids+1)) + return -ENOMEM; + + pidrefs[n_pids++] = TAKE_PIDREF(p); + } + + if (n_pids == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "No processes can be migrated to auxiliary scope."); + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "t", &flags); + if (r < 0) + return r; + + if (flags != 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero."); + + r = manager_load_unit(m, name, NULL, error, &scope); + if (r < 0) + return r; + + if (!unit_is_pristine(scope)) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, + "Unit %s was already loaded or has a fragment file.", name); + + r = unit_set_slice(scope, UNIT_GET_SLICE(from)); + if (r < 0) + return r; + + cc = unit_get_cgroup_context(scope); + + r = cgroup_context_copy(cc, unit_get_cgroup_context(from)); + if (r < 0) + return r; + + r = unit_make_transient(scope); + if (r < 0) + return r; + + r = bus_unit_set_properties(scope, message, UNIT_RUNTIME, true, error); + if (r < 0) + return r; + + FOREACH_ARRAY(p, pidrefs, n_pids) { + r = unit_pid_attachable(scope, p, error); + if (r < 0) + return r; + + r = unit_watch_pidref(scope, p, /* exclusive= */ false); + if (r < 0 && r != -EEXIST) + return r; + } + + /* Now load the missing bits of the unit we just created */ + unit_add_to_load_queue(scope); + manager_dispatch_load_queue(m); + + *ret_scope = TAKE_PTR(scope); + + return 1; +} + +static int method_start_aux_scope(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = ASSERT_PTR(userdata); + Unit *u = NULL; /* avoid false maybe-uninitialized warning */ + int r; + + assert(message); + + r = mac_selinux_access_check(message, "start", error); + if (r < 0) + return r; + + r = bus_verify_manage_units_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = aux_scope_from_message(m, message, &u, error); + if (r < 0) + return r; + + return bus_unit_queue_job(message, u, JOB_START, JOB_REPLACE, 0, error); +} + const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_VTABLE_START(0), @@ -2948,6 +3064,7 @@ const sd_bus_vtable bus_manager_vtable[] = { BUS_PROPERTY_DUAL_TIMESTAMP("InitRDTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD]), SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("UserspaceTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_USERSPACE]), SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("FinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("ShutdownStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_SHUTDOWN_START]), SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("SecurityStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_SECURITY_START]), SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("SecurityFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_SECURITY_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("GeneratorsStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_GENERATORS_START]), SD_BUS_VTABLE_PROPERTY_CONST), @@ -3045,6 +3162,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("DefaultOOMPolicy", "s", bus_property_get_oom_policy, offsetof(Manager, defaults.oom_policy), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultOOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CtrlAltDelBurstAction", "s", bus_property_get_emergency_action, offsetof(Manager, cad_burst_action), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SoftRebootsCount", "u", bus_property_get_unsigned, offsetof(Manager, soft_reboots_count), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_METHOD_WITH_ARGS("GetUnit", SD_BUS_ARGS("s", name), @@ -3491,6 +3609,11 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_RESULT("a(suuutuusu)", entries), method_dump_unit_descriptor_store, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("StartAuxiliaryScope", + SD_BUS_ARGS("s", name, "ah", pidfds, "t", flags, "a(sv)", properties), + SD_BUS_RESULT("o", job), + method_start_aux_scope, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL_WITH_ARGS("UnitNew", SD_BUS_ARGS("s", id, "o", unit), |