X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fservice.c;h=5d82e9b545172516147b59956ac8e386bfd64852;hp=1c04ed338a8cc60c79324a9bf9c096d58ad4201b;hb=2e9d6c1251abc0af9aa5bc53610bd522d55be776;hpb=5430f7f2bc7330f3088b894166bf3524a067e3d8 diff --git a/src/core/service.c b/src/core/service.c index 1c04ed338..5d82e9b54 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -38,6 +38,7 @@ #include "bus-errors.h" #include "exit-status.h" #include "def.h" +#include "path-util.h" #include "util.h" #include "utf8.h" @@ -109,12 +110,14 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { static void service_init(Unit *u) { Service *s = SERVICE(u); + int i; assert(u); assert(u->load_state == UNIT_STUB); s->timeout_usec = DEFAULT_TIMEOUT_USEC; s->restart_usec = DEFAULT_RESTART_USEC; + s->type = _SERVICE_TYPE_INVALID; s->watchdog_watch.type = WATCH_INVALID; @@ -127,6 +130,9 @@ static void service_init(Unit *u) { s->guess_main_pid = true; exec_context_init(&s->exec_context); + for (i = 0; i < RLIMIT_NLIMITS; i++) + if (UNIT(s)->manager->rlimit[i]) + s->exec_context.rlimit[i] = newdup(struct rlimit, UNIT(s)->manager->rlimit[i], 1); RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5); @@ -721,7 +727,7 @@ static int service_load_sysv_path(Service *s, const char *path) { goto finish; } - r = sysv_translate_facility(n, file_name_from_path(path), &m); + r = sysv_translate_facility(n, path_get_file_name(path), &m); free(n); if (r < 0) @@ -771,7 +777,7 @@ static int service_load_sysv_path(Service *s, const char *path) { goto finish; } - r = sysv_translate_facility(n, file_name_from_path(path), &m); + r = sysv_translate_facility(n, path_get_file_name(path), &m); if (r < 0) { log_error("[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", path, line, n, strerror(-r)); @@ -1135,6 +1141,9 @@ static int service_verify(Service *s) { return -EINVAL; } + if (s->bus_name && s->type != SERVICE_DBUS) + log_warning("%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id); + if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) { log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); return -EINVAL; @@ -1213,6 +1222,9 @@ static int service_load(Unit *u) { /* This is a new unit? Then let's add in some extras */ if (u->load_state == UNIT_LOADED) { + if (s->type == _SERVICE_TYPE_INVALID) + s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE; + service_fix_output(s); if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) @@ -1686,6 +1698,7 @@ static int service_spawn( bool apply_chroot, bool apply_tty_stdin, bool set_notify_socket, + bool is_control, pid_t *_pid) { pid_t pid; @@ -1767,6 +1780,8 @@ static int service_spawn( UNIT(s)->manager->confirm_spawn, UNIT(s)->cgroup_bondings, UNIT(s)->cgroup_attributes, + is_control ? "control" : NULL, + s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL, &pid); if (r < 0) @@ -1886,15 +1901,17 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) { s->control_command_id = SERVICE_EXEC_STOP_POST; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + true, + false, + true, + &s->control_pid); + if (r < 0) goto fail; @@ -1952,7 +1969,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) goto fail; - if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) { + r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL); + if (r < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) log_warning("Failed to kill control group: %s", strerror(-r)); } else if (r > 0) @@ -2001,15 +2019,17 @@ static void service_enter_stop(Service *s, ServiceResult f) { if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) { s->control_command_id = SERVICE_EXEC_STOP; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + false, + false, + true, + &s->control_pid); + if (r < 0) goto fail; service_set_state(s, SERVICE_STOP); @@ -2054,15 +2074,17 @@ static void service_enter_start_post(Service *s) { if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) { s->control_command_id = SERVICE_EXEC_START_POST; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + false, + false, + true, + &s->control_pid); + if (r < 0) goto fail; service_set_state(s, SERVICE_START_POST); @@ -2094,7 +2116,7 @@ static void service_enter_start(Service *s) { /* We want to ensure that nobody leaks processes from * START_PRE here, so let's go on a killing spree, People * should not spawn long running processes from START_PRE. */ - cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL); + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); if (s->type == SERVICE_FORKING) { s->control_command_id = SERVICE_EXEC_START; @@ -2108,18 +2130,20 @@ static void service_enter_start(Service *s) { c = s->main_command = s->exec_command[SERVICE_EXEC_START]; } - if ((r = service_spawn(s, - c, - s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY, - true, - true, - true, - true, - s->notify_access != NOTIFY_NONE, - &pid)) < 0) + r = service_spawn(s, + c, + s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY, + true, + true, + true, + true, + s->notify_access != NOTIFY_NONE, + false, + &pid); + if (r < 0) goto fail; - if (s->type == SERVICE_SIMPLE) { + if (s->type == SERVICE_SIMPLE || s->type == SERVICE_IDLE) { /* For simple services we immediately start * the START_POST binaries. */ @@ -2168,19 +2192,21 @@ static void service_enter_start_pre(Service *s) { /* Before we start anything, let's clear up what might * be left from previous runs. */ - cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL); + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); s->control_command_id = SERVICE_EXEC_START_PRE; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + true, + false, + true, + &s->control_pid); + if (r < 0) goto fail; service_set_state(s, SERVICE_START_PRE); @@ -2206,6 +2232,8 @@ static void service_enter_restart(Service *s) { if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0) goto fail; + + return; } /* Any units that are bound to this service must also be @@ -2236,15 +2264,17 @@ static void service_enter_reload(Service *s) { if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) { s->control_command_id = SERVICE_EXEC_RELOAD; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + false, + false, + true, + &s->control_pid); + if (r < 0) goto fail; service_set_state(s, SERVICE_RELOAD); @@ -2271,16 +2301,18 @@ static void service_run_next_control(Service *s) { s->control_command = s->control_command->command_next; service_unwatch_control_pid(s); - if ((r = service_spawn(s, - s->control_command, - true, - 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, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + 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) goto fail; return; @@ -2313,15 +2345,17 @@ static void service_run_next_main(Service *s) { s->main_command = s->main_command->command_next; service_unwatch_main_pid(s); - if ((r = service_spawn(s, - s->main_command, - false, - true, - true, - true, - true, - s->notify_access != NOTIFY_NONE, - &pid)) < 0) + r = service_spawn(s, + s->main_command, + false, + true, + true, + true, + true, + s->notify_access != NOTIFY_NONE, + false, + &pid); + if (r < 0) goto fail; service_set_main_pid(s, pid); @@ -2402,7 +2436,16 @@ static int service_start(Unit *u) { s->state == SERVICE_START_POST) return 0; - assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED || s->state == SERVICE_AUTO_RESTART); + /* A service that will be restarted must be stopped first to + * trigger BindTo and/or OnFailure dependencies. If a user + * does not want to wait for the holdoff time to elapse, the + * service should be manually restarted, not started. */ + if (s->state == SERVICE_AUTO_RESTART) { + log_warning("%s automatic restart is pending, must be stopped before issuing start request.", UNIT(s)->id); + return -ECANCELED; + } + + assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED); /* Make sure we don't enter a busy loop of some kind. */ r = service_start_limit_test(s); @@ -2426,8 +2469,7 @@ static int service_stop(Unit *u) { assert(s); - /* This is a user request, so don't do restarts on this - * shutdown. */ + /* Don't create restart jobs from here. */ s->forbid_restart = true; /* Already on it */ @@ -2439,9 +2481,9 @@ static int service_stop(Unit *u) { s->state == SERVICE_FINAL_SIGKILL) return 0; - /* Don't allow a restart */ + /* A restart will be scheduled or is in progress. */ if (s->state == SERVICE_AUTO_RESTART) { - service_set_state(s, SERVICE_DEAD); + service_enter_dead(s, SERVICE_SUCCESS, false); return 0; } @@ -2897,6 +2939,11 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { if (f != SERVICE_SUCCESS) s->result = f; + /* Immediately get rid of the cgroup, so that the + * kernel doesn't delay the cgroup empty messages for + * the service cgroup any longer than necessary */ + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); + if (s->control_command && s->control_command->command_next && f == SERVICE_SUCCESS) { @@ -3647,8 +3694,8 @@ static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusErro r = q; goto finish; } - - if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0) + q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL); + if (q < 0) if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; } @@ -3695,7 +3742,8 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = { [SERVICE_FORKING] = "forking", [SERVICE_ONESHOT] = "oneshot", [SERVICE_DBUS] = "dbus", - [SERVICE_NOTIFY] = "notify" + [SERVICE_NOTIFY] = "notify", + [SERVICE_IDLE] = "idle" }; DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); @@ -3746,7 +3794,6 @@ const UnitVTable service_vtable = { "Unit\0" "Service\0" "Install\0", - .show_status = true, .init = service_init, .done = service_done, @@ -3792,6 +3839,23 @@ const UnitVTable service_vtable = { .bus_invalidating_properties = bus_service_invalidating_properties, #ifdef HAVE_SYSV_COMPAT - .enumerate = service_enumerate + .enumerate = service_enumerate, #endif + .status_message_formats = { + .starting_stopping = { + [0] = "Starting %s...", + [1] = "Stopping %s...", + }, + .finished_start_job = { + [JOB_DONE] = "Started %s.", + [JOB_FAILED] = "Failed to start %s.", + [JOB_DEPENDENCY] = "Dependency failed for %s.", + [JOB_TIMEOUT] = "Timed out starting %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Stopped %s.", + [JOB_FAILED] = "Stopped (with error) %s.", + [JOB_TIMEOUT] = "Timed out stopping %s.", + }, + }, };