X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fcore%2Fservice.c;h=d160c4e93b308ba13074396b9fc562ec09d59434;hb=056f95d0a70413e10e4a4ea9966baabb5a1450ed;hp=322193879320d982f2044f3f673f55458b6abb9b;hpb=308d72dc1e2106f94ae637e2ea510e8d466d2af1;p=elogind.git diff --git a/src/core/service.c b/src/core/service.c index 322193879..d160c4e93 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -23,9 +23,6 @@ #include #include #include -#include -#include -#include #include "async.h" #include "manager.h" @@ -48,6 +45,7 @@ #include "fileio.h" #include "bus-error.h" #include "bus-util.h" +#include "bus-kernel.h" static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_DEAD] = UNIT_INACTIVE, @@ -105,6 +103,7 @@ static void service_init(Unit *u) { s->restart_usec = u->manager->default_restart_usec; s->type = _SERVICE_TYPE_INVALID; s->socket_fd = -1; + s->bus_endpoint_fd = -1; s->guess_main_pid = true; RATELIMIT_INIT(s->start_limit, u->manager->default_start_limit_interval, u->manager->default_start_limit_burst); @@ -276,6 +275,7 @@ static void service_done(Unit *u) { s->bus_name = NULL; } + s->bus_endpoint_fd = safe_close(s->bus_endpoint_fd); service_close_socket_fd(s); service_connection_unref(s); @@ -313,14 +313,23 @@ static int service_verify(Service *s) { if (UNIT(s)->load_state != UNIT_LOADED) return 0; - if (!s->exec_command[SERVICE_EXEC_START]) { - log_error_unit(UNIT(s)->id, "%s lacks ExecStart setting. Refusing.", UNIT(s)->id); + if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) { + log_error_unit(UNIT(s)->id, "%s lacks both ExecStart= and ExecStop= setting. Refusing.", UNIT(s)->id); return -EINVAL; } - if (s->type != SERVICE_ONESHOT && - s->exec_command[SERVICE_EXEC_START]->command_next) { - log_error_unit(UNIT(s)->id, "%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); + if (s->type != SERVICE_ONESHOT && !s->exec_command[SERVICE_EXEC_START]) { + log_error_unit(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); + return -EINVAL; + } + + if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) { + log_error_unit(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.", UNIT(s)->id); + return -EINVAL; + } + + if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next) { + log_error_unit(UNIT(s)->id, "%s has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); return -EINVAL; } @@ -410,8 +419,15 @@ static int service_load(Unit *u) { if (r < 0) return r; - if (s->type == _SERVICE_TYPE_INVALID) - s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE; + if (s->type == _SERVICE_TYPE_INVALID) { + /* Figure out a type automatically */ + if (s->bus_name) + s->type = SERVICE_DBUS; + else if (s->exec_command[SERVICE_EXEC_START]) + s->type = SERVICE_SIMPLE; + else + s->type = SERVICE_ONESHOT; + } /* Oneshot services have disabled start timeout by default */ if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined) @@ -862,12 +878,11 @@ fail: static int service_spawn( Service *s, ExecCommand *c, - bool timeout, + usec_t timeout, bool pass_fds, bool apply_permissions, bool apply_chroot, bool apply_tty_stdin, - bool set_notify_socket, bool is_control, pid_t *_pid) { @@ -876,9 +891,17 @@ static int service_spawn( int *fds = NULL; _cleanup_free_ int *fdsbuf = NULL; unsigned n_fds = 0, n_env = 0; + _cleanup_free_ char *bus_endpoint_path = NULL; _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL; const char *path; + ExecParameters exec_params = { + .apply_permissions = apply_permissions, + .apply_chroot = apply_chroot, + .apply_tty_stdin = apply_tty_stdin, + .bus_endpoint_fd = -1, + .selinux_context_net = s->socket_fd_selinux_context_net + }; assert(s); assert(c); @@ -907,8 +930,8 @@ static int service_spawn( } } - if (timeout && s->timeout_start_usec > 0) { - r = service_arm_timer(s, s->timeout_start_usec); + if (timeout > 0) { + r = service_arm_timer(s, timeout); if (r < 0) goto fail; } else @@ -924,7 +947,7 @@ static int service_spawn( goto fail; } - if (set_notify_socket) + if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE) if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) { r = -ENOMEM; goto fail; @@ -954,21 +977,37 @@ static int service_spawn( } else path = UNIT(s)->cgroup_path; +#ifdef ENABLE_KDBUS + if (s->exec_context.bus_endpoint) { + r = bus_kernel_create_endpoint(UNIT(s)->manager->running_as == SYSTEMD_SYSTEM ? "system" : "user", + UNIT(s)->id, &bus_endpoint_path); + if (r < 0) + goto fail; + + /* Pass the fd to the exec_params so that the child process can upload the policy. + * Keep a reference to the fd in the service, so the endpoint is kept alive as long + * as the service is running. */ + exec_params.bus_endpoint_fd = s->bus_endpoint_fd = r; + } +#endif + + exec_params.argv = argv; + exec_params.fds = fds; + exec_params.n_fds = n_fds; + exec_params.environment = final_env; + exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; + exec_params.cgroup_path = path; + exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); + exec_params.unit_id = UNIT(s)->id; + exec_params.watchdog_usec = s->watchdog_usec; + exec_params.bus_endpoint_path = bus_endpoint_path; + if (s->type == SERVICE_IDLE) + exec_params.idle_pipe = UNIT(s)->manager->idle_pipe; + r = exec_spawn(c, - argv, &s->exec_context, - fds, n_fds, - final_env, - apply_permissions, - apply_chroot, - apply_tty_stdin, - UNIT(s)->manager->confirm_spawn, - UNIT(s)->manager->cgroup_supported, - path, - manager_get_runtime_prefix(UNIT(s)->manager), - UNIT(s)->id, - s->watchdog_usec, - s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL, + &exec_params, s->exec_runtime, &pid); if (r < 0) @@ -1036,8 +1075,6 @@ static int cgroup_good(Service *s) { return !r; } -static int service_execute_action(Service *s, FailureAction action, const char *reason, bool log_action_none); - static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) { int r; assert(s); @@ -1047,8 +1084,10 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); - if (s->result != SERVICE_SUCCESS) - service_execute_action(s, s->failure_action, "failed", false); + if (s->result != SERVICE_SUCCESS) { + log_warning_unit(UNIT(s)->id, "%s failed.", UNIT(s)->id); + failure_action(UNIT(s)->manager, s->failure_action, s->reboot_arg); + } if (allow_restart && !s->forbid_restart && @@ -1108,12 +1147,11 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, - true, + s->timeout_stop_usec, false, !s->permissions_start_only, !s->root_directory_start_only, true, - false, true, &s->control_pid); if (r < 0) @@ -1187,7 +1225,8 @@ static void service_enter_stop_by_notify(Service *s) { if (s->timeout_stop_usec > 0) service_arm_timer(s, s->timeout_stop_usec); - service_set_state(s, SERVICE_STOP); + /* The service told us it's stopping, so it's as if we SIGTERM'd it. */ + service_set_state(s, SERVICE_STOP_SIGTERM); } static void service_enter_stop(Service *s, ServiceResult f) { @@ -1207,12 +1246,11 @@ static void service_enter_stop(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, - true, + s->timeout_stop_usec, false, !s->permissions_start_only, !s->root_directory_start_only, false, - false, true, &s->control_pid); if (r < 0) @@ -1270,12 +1308,11 @@ static void service_enter_start_post(Service *s) { r = service_spawn(s, s->control_command, - true, + s->timeout_start_usec, false, !s->permissions_start_only, !s->root_directory_start_only, false, - false, true, &s->control_pid); if (r < 0) @@ -1309,9 +1346,6 @@ static void service_enter_start(Service *s) { assert(s); - assert(s->exec_command[SERVICE_EXEC_START]); - assert(!s->exec_command[SERVICE_EXEC_START]->command_next || s->type == SERVICE_ONESHOT); - service_unwatch_control_pid(s); service_unwatch_main_pid(s); @@ -1332,15 +1366,19 @@ static void service_enter_start(Service *s) { c = s->main_command = s->exec_command[SERVICE_EXEC_START]; } + if (!c) { + assert(s->type == SERVICE_ONESHOT); + service_enter_start_post(s); + return; + } + r = service_spawn(s, c, - s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || - s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT, + IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_ONESHOT) ? s->timeout_start_usec : 0, true, true, true, true, - s->notify_access != NOTIFY_NONE, false, &pid); if (r < 0) @@ -1401,12 +1439,11 @@ static void service_enter_start_pre(Service *s) { r = service_spawn(s, s->control_command, - true, + s->timeout_start_usec, false, !s->permissions_start_only, !s->root_directory_start_only, true, - false, true, &s->control_pid); if (r < 0) @@ -1482,12 +1519,11 @@ static void service_enter_reload(Service *s) { r = service_spawn(s, s->control_command, - true, + s->timeout_start_usec, false, !s->permissions_start_only, !s->root_directory_start_only, false, - false, true, &s->control_pid); if (r < 0) @@ -1519,13 +1555,12 @@ static void service_run_next_control(Service *s) { r = service_spawn(s, s->control_command, - true, + IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec, false, !s->permissions_start_only, !s->root_directory_start_only, s->control_command_id == SERVICE_EXEC_START_PRE || s->control_command_id == SERVICE_EXEC_STOP_POST, - false, true, &s->control_pid); if (r < 0) @@ -1563,12 +1598,11 @@ static void service_run_next_main(Service *s) { r = service_spawn(s, s->main_command, + s->timeout_start_usec, true, true, true, true, - true, - s->notify_access != NOTIFY_NONE, false, &pid); if (r < 0) @@ -1583,67 +1617,15 @@ fail: service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } -static int service_execute_action(Service *s, FailureAction action, const char *reason, bool log_action_none) { - assert(s); - - if (action == SERVICE_FAILURE_ACTION_REBOOT || - action == SERVICE_FAILURE_ACTION_REBOOT_FORCE) - update_reboot_param_file(s->reboot_arg); - - switch (action) { - - case SERVICE_FAILURE_ACTION_NONE: - if (log_action_none) - log_warning_unit(UNIT(s)->id, "%s %s, refusing to start.", UNIT(s)->id, reason); - break; - - case SERVICE_FAILURE_ACTION_REBOOT: { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - log_warning_unit(UNIT(s)->id, "%s %s, rebooting.", UNIT(s)->id, reason); - - r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL); - if (r < 0) - log_error_unit(UNIT(s)->id, "Failed to reboot: %s.", bus_error_message(&error, r)); - - break; - } - - case SERVICE_FAILURE_ACTION_REBOOT_FORCE: - log_warning_unit(UNIT(s)->id, "%s %s, forcibly rebooting.", UNIT(s)->id, reason); - UNIT(s)->manager->exit_code = MANAGER_REBOOT; - break; - - case SERVICE_FAILURE_ACTION_REBOOT_IMMEDIATE: - log_warning_unit(UNIT(s)->id, "%s %s, rebooting immediately.", UNIT(s)->id, reason); - - sync(); - - if (s->reboot_arg) { - log_info("Rebooting with argument '%s'.", s->reboot_arg); - syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, s->reboot_arg); - } - - log_info("Rebooting."); - reboot(RB_AUTOBOOT); - break; - - default: - log_error_unit(UNIT(s)->id, "failure action=%i", action); - assert_not_reached("Unknown FailureAction."); - } - - return -ECANCELED; -} - static int service_start_limit_test(Service *s) { assert(s); if (ratelimit_test(&s->start_limit)) return 0; - return service_execute_action(s, s->start_limit_action, "start request repeated too quickly", true); + log_warning_unit(UNIT(s)->id, "start request repeated too quickly for %s", UNIT(s)->id); + + return failure_action(UNIT(s)->manager, s->start_limit_action, s->reboot_arg); } static int service_start(Unit *u) { @@ -1801,6 +1783,15 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item_format(u, f, "socket-fd", "%i", copy); } + if (s->bus_endpoint_fd >= 0) { + int copy; + + if ((copy = fdset_put_dup(fds, s->bus_endpoint_fd)) < 0) + return copy; + + unit_serialize_item_format(u, f, "endpoint-fd", "%i", copy); + } + if (s->main_exec_status.pid > 0) { unit_serialize_item_format(u, f, "main-exec-status-pid", PID_FMT, s->main_exec_status.pid); @@ -1910,10 +1901,18 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) log_debug_unit(u->id, "Failed to parse socket-fd value %s", value); else { - asynchronous_close(s->socket_fd); s->socket_fd = fdset_remove(fds, fd); } + } else if (streq(key, "endpoint-fd")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_debug_unit(u->id, "Failed to parse endpoint-fd value %s", value); + else { + safe_close(s->bus_endpoint_fd); + s->bus_endpoint_fd = fdset_remove(fds, fd); + } } else if (streq(key, "main-exec-status-pid")) { pid_t pid; @@ -2730,7 +2729,7 @@ static void service_bus_name_owner_change( /* Try to acquire PID from bus service */ - r = sd_bus_get_owner(u->manager->api_bus, name, SD_BUS_CREDS_PID, &creds); + r = sd_bus_get_name_creds(u->manager->api_bus, name, SD_BUS_CREDS_PID, &creds); if (r >= 0) r = sd_bus_creds_get_pid(creds, &pid); if (r >= 0) { @@ -2742,7 +2741,7 @@ static void service_bus_name_owner_change( } } -int service_set_socket_fd(Service *s, int fd, Socket *sock) { +int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context_net) { _cleanup_free_ char *peer = NULL; int r; @@ -2780,6 +2779,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock) { } s->socket_fd = fd; + s->socket_fd_selinux_context_net = selinux_context_net; unit_ref_set(&s->accept_socket, UNIT(sock)); @@ -2890,14 +2890,6 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); -static const char* const failure_action_table[_SERVICE_FAILURE_ACTION_MAX] = { - [SERVICE_FAILURE_ACTION_NONE] = "none", - [SERVICE_FAILURE_ACTION_REBOOT] = "reboot", - [SERVICE_FAILURE_ACTION_REBOOT_FORCE] = "reboot-force", - [SERVICE_FAILURE_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate" -}; -DEFINE_STRING_TABLE_LOOKUP(failure_action, FailureAction); - const UnitVTable service_vtable = { .object_size = sizeof(Service), .exec_context_offset = offsetof(Service, exec_context),