service.c revision fccd44ec3a7abd305b558d39f54b5ef223f00b68
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose This file is part of systemd.
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose Copyright 2010 Lennart Poettering
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose systemd is free software; you can redistribute it and/or modify it
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose under the terms of the GNU Lesser General Public License as published by
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose the Free Software Foundation; either version 2.1 of the License, or
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose (at your option) any later version.
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose systemd is distributed in the hope that it will be useful, but
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose WITHOUT ANY WARRANTY; without even the implied warranty of
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose Lesser General Public License for more details.
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose You should have received a copy of the GNU Lesser General Public License
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose along with systemd; If not, see <http://www.gnu.org/licenses/>.
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose#define DEFAULT_SYSV_TIMEOUT_USEC (5*USEC_PER_MINUTE)
a0ab15ceb80290db80c2052520830a95390de385Sumit Bosestatic const struct {
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose const char *path;
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose const char *target;
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose /* Standard SysV runlevels for start-up */
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose { "rc2.d", SPECIAL_RUNLEVEL2_TARGET, RUNLEVEL_UP },
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose { "rc3.d", SPECIAL_RUNLEVEL3_TARGET, RUNLEVEL_UP },
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP },
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP },
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose /* Standard SysV runlevels for shutdown */
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN },
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN }
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose /* Note that the order here matters, as we read the
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose directories in this order, and we want to make sure that
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose sysv_start_priority is known when we first load the
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose unit. And that value we only know from S links. Hence
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose UP must be read before DOWN */
a0ab15ceb80290db80c2052520830a95390de385Sumit Bosestatic const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose/* For Type=idle we never want to delay any other jobs, hence we
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose * consider idle jobs active as soon as we start working on them */
a0ab15ceb80290db80c2052520830a95390de385Sumit Bosestatic const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = {
a0ab15ceb80290db80c2052520830a95390de385Sumit Bosestatic int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
a0ab15ceb80290db80c2052520830a95390de385Sumit Bosestatic int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
a0ab15ceb80290db80c2052520830a95390de385Sumit Bosestatic int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata);
a0ab15ceb80290db80c2052520830a95390de385Sumit Bosestatic void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose s->timeout_start_usec = u->manager->default_timeout_start_usec;
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose s->restart_usec = u->manager->default_restart_usec;
a0ab15ceb80290db80c2052520830a95390de385Sumit Bose s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
a0ab15ceb80290db80c2052520830a95390de385Sumit Bosestatic void service_unwatch_control_pid(Service *s) {
assert(s);
if (s->main_pid <= 0)
s->main_pid = 0;
if (!s->pid_file_pathspec)
assert(s);
return -EINVAL;
return -EINVAL;
s->main_pid_known = true;
"%s: Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.",
s->main_pid_alien = true;
s->main_pid_alien = false;
assert(s);
if (s->socket_fd < 0)
assert(s);
assert(s);
assert(s);
if (s->watchdog_usec <= 0)
if (s->watchdog_event_source) {
r = sd_event_source_set_time(s->watchdog_event_source, s->watchdog_timestamp.monotonic + s->watchdog_usec);
r = sd_event_add_monotonic(UNIT(s)->manager->event, s->watchdog_timestamp.monotonic + s->watchdog_usec, 0, service_dispatch_watchdog, s, &s->watchdog_event_source);
log_warning_unit(UNIT(s)->id, "%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r));
assert(s);
assert(s);
#ifdef HAVE_SYSV_COMPAT
if (s->bus_name) {
assert(s);
if (s->timer_event_source) {
return sd_event_add_monotonic(UNIT(s)->manager->event, now(CLOCK_MONOTONIC) + usec, 0, service_dispatch_timer, s, &s->timer_event_source);
#ifdef HAVE_SYSV_COMPAT
return NULL;
static const char * const table[] = {
return log_oom();
goto finish;
if (!unit_prefix_is_valid(n))
return -EINVAL;
r = sysv_translate_name(n);
return -ENOMEM;
*_r = r;
assert(s);
if (s->sysv_start_priority < 0)
Service *t;
if (t->sysv_start_priority < 0)
d = UNIT_AFTER;
d = UNIT_BEFORE;
d = UNIT_AFTER;
d = UNIT_BEFORE;
ExecCommand *c;
return NULL;
free(c);
return NULL;
free(c);
return NULL;
ExecCommand *c;
assert(s);
return -ENOMEM;
return -ENOMEM;
if (supports_reload) {
return -ENOMEM;
FILE *f;
Unit *u;
unsigned line = 0;
LSB,
char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
bool supports_reload = false;
assert(s);
u = UNIT(s);
goto finish;
r = -errno;
goto finish;
if (!u->source_path) {
r = -ENOMEM;
goto finish;
goto finish;
s->is_sysv = true;
while (!feof(f)) {
char l[LINE_MAX], *t;
if (!fgets(l, sizeof(l), f)) {
if (feof(f))
r = -errno;
goto finish;
line++;
t = strstrip(l);
if (usage_contains_reload(t)) {
supports_reload = true;
s->sysv_has_lsb = true;
int start_priority;
if (!(d = strdup(k))) {
r = -ENOMEM;
goto finish;
s->sysv_runlevels = d;
if (!(d = strdup(j))) {
r = -ENOMEM;
goto finish;
d = NULL;
char *fn;
r = -ENOMEM;
goto finish;
if ((j = strstrip(t)) && *j) {
char *d = NULL;
d = strdup(j);
r = -ENOMEM;
goto finish;
size_t z;
if (!(n = strndup(w, z))) {
r = -ENOMEM;
goto finish;
free(n);
goto finish;
r = unit_merge_by_name(u, m);
free(m);
size_t z;
if (!(n = strndup(w, z))) {
r = -ENOMEM;
goto finish;
free(n);
free(n);
r = unit_add_dependency_by_name(u, startswith_no_case(t, "X-Start-Before:") ? UNIT_BEFORE : UNIT_AFTER, m, NULL, true);
free(m);
if (!(d = strdup(k))) {
r = -ENOMEM;
goto finish;
s->sysv_runlevels = d;
if (!(d = strdup(j))) {
r = -ENOMEM;
goto finish;
d = NULL;
long_description = d;
if (!(d = strdup(j))) {
r = -ENOMEM;
goto finish;
d = NULL;
short_description = d;
if ((j = strstrip(t)) && *j) {
char *d = NULL;
if (long_description)
d = strdup(j);
r = -ENOMEM;
goto finish;
long_description = d;
goto finish;
s->timeout_start_usec = 0;
s->timeout_stop_usec = 0;
s->guess_main_pid = false;
if (short_description)
else if (chkconfig_description)
else if (long_description)
if (description) {
r = -ENOMEM;
goto finish;
u->description = d;
if (s->sysv_start_priority_from_rcnd >= 0)
fclose(f);
assert(s);
return -ENOENT;
char *path;
if (!path)
return -ENOMEM;
Iterator i;
assert(s);
if ((r = service_load_sysv_name(s, t)) < 0)
if ((r = service_load_sysv_name(s, t)) < 0)
assert(s);
return -EINVAL;
"%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
return -EINVAL;
"%s has Restart setting other than no, which isn't allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
return -EINVAL;
return -EINVAL;
return -EINVAL;
assert(s);
assert(s);
assert(s);
r = unit_load_fragment(u);
#ifdef HAVE_SYSV_COMPAT
r = service_load_sysv(s);
return -ENOENT;
r = unit_load_dropin(u);
s->timeout_start_usec = 0;
r = unit_add_default_slice(u);
#ifdef HAVE_SYSV_COMPAT
r = sysv_fix_order(s);
if (s->bus_name) {
r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true);
r = service_add_default_dependencies(s);
return service_verify(s);
const char *prefix2;
assert(s);
fprintf(f,
if (s->control_pid > 0)
fprintf(f,
if (s->main_pid > 0)
fprintf(f,
if (s->pid_file)
fprintf(f,
if (s->bus_name)
fprintf(f,
for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) {
if (!s->exec_command[c])
#ifdef HAVE_SYSV_COMPAT
if (s->is_sysv)
fprintf(f,
if (s->sysv_start_priority >= 0)
fprintf(f,
if (s->sysv_runlevels)
if (s->status_text)
assert(s);
if (!s->pid_file)
return -ENOENT;
if (may_warn)
if (may_warn)
if (may_warn)
return -ESRCH;
if (s->main_pid_known) {
s->main_pid_known = false;
assert(s);
if (s->main_pid_known)
if (!s->guess_main_pid)
if (pid <= 0)
return -ENOENT;
assert(s);
m->n_on_console --;
if (m->n_on_console == 0)
m->no_console_output = false;
log_debug_unit(UNIT(s)->id, "%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state));
assert(s);
usec_t k;
k = IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec;
r = service_arm_timer(s, k);
((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) ||
Iterator i;
unsigned rn_fds = 0;
Unit *u;
assert(s);
if (s->socket_fd >= 0)
int *cfds;
unsigned cn_fds;
goto fail;
if (!cfds)
if (!rfds) {
r = -ENOMEM;
goto fail;
rfds = t;
fail:
static int service_spawn(
Service *s,
ExecCommand *c,
bool timeout,
bool pass_fds,
bool apply_permissions,
bool apply_chroot,
bool apply_tty_stdin,
bool set_notify_socket,
bool is_control,
_cleanup_strv_free_ char
const char *path;
assert(s);
assert(c);
goto fail;
if (pass_fds ||
if (s->socket_fd >= 0) {
goto fail;
goto fail;
goto fail;
if (!our_env) {
r = -ENOMEM;
goto fail;
if (set_notify_socket)
r = -ENOMEM;
goto fail;
if (s->main_pid > 0)
r = -ENOMEM;
goto fail;
r = -ENOMEM;
goto fail;
if (!final_env) {
r = -ENOMEM;
goto fail;
r = exec_spawn(c,
argv,
&s->exec_context,
path,
s->watchdog_usec,
s->exec_runtime,
&pid);
goto fail;
goto fail;
fail:
if (timeout)
assert(s);
if (s->main_pid_known) {
return s->main_pid > 0;
return -EAGAIN;
assert(s);
return s->control_pid > 0;
assert(s);
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
if (allow_restart &&
!s->forbid_restart &&
goto fail;
s->forbid_restart = false;
if (s->pid_file)
fail:
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
r = unit_kill_context(
UNIT(s),
&s->kill_context,
s->main_pid,
s->control_pid,
s->main_pid_alien);
goto fail;
if (s->timeout_stop_usec > 0) {
goto fail;
fail:
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
else if (s->remain_after_exit)
assert(s);
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
ExecCommand *c;
assert(s);
r = service_spawn(s,
&pid);
goto fail;
fail:
assert(s);
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
goto fail;
goto fail;
fail:
assert(s);
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
r = service_spawn(s,
s->main_command,
&pid);
goto fail;
fail:
assert(s);
switch (s->start_limit_action) {
case SERVICE_START_LIMIT_NONE:
case SERVICE_START_LIMIT_REBOOT: {
sync();
return -ECANCELED;
assert(s);
return -EAGAIN;
return -EAGAIN;
r = service_start_limit_test(s);
s->main_pid_known = false;
s->main_pid_alien = false;
s->forbid_restart = false;
assert(s);
s->forbid_restart = true;
assert(s);
assert(s);
assert(u);
assert(f);
if (s->control_pid > 0)
s->control_pid);
if (s->status_text)
if (s->control_command_id >= 0)
if (s->socket_fd >= 0) {
int copy;
return copy;
if (s->forbid_restart)
assert(u);
if (state < 0)
else if (f != SERVICE_SUCCESS)
s->result = f;
else if (f != SERVICE_SUCCESS)
s->reload_result = f;
s->main_pid_known = b;
log_oom();
s->status_text = t;
if (id < 0)
int fd;
if (s->socket_fd >= 0)
s->forbid_restart = b;
assert(u);
assert(u);
assert(s);
if (cgroup_good(s) > 0 ||
main_pid_good(s) > 0 ||
control_pid_good(s) > 0)
#ifdef HAVE_SYSV_COMPAT
if (s->is_sysv)
assert(s);
return (s->socket_fd < 0);
r = service_load_pid_file(s, false);
goto fail;
fail:
if (!ps)
return -ENOMEM;
return -ENOMEM;
return service_watch_pid_file(s);
Service *s;
assert(p);
assert(s);
goto fail;
if (service_retry_pid_file(s) == 0)
if (service_watch_pid_file(s) < 0)
goto fail;
fail:
assert(s);
f = SERVICE_SUCCESS;
if (service_load_pid_file(s, false) == 0)
s->main_pid = 0;
if (s->main_command) {
f = SERVICE_SUCCESS;
f = SERVICE_SUCCESS;
u->id,
NULL);
if (f != SERVICE_SUCCESS)
s->result = f;
if (s->main_command &&
f == SERVICE_SUCCESS) {
switch (s->state) {
case SERVICE_START_POST:
case SERVICE_RELOAD:
case SERVICE_STOP:
case SERVICE_START:
if (f == SERVICE_SUCCESS)
case SERVICE_RUNNING:
service_enter_running(s, f);
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
if (!control_pid_good(s))
service_enter_stop_post(s, f);
case SERVICE_STOP_POST:
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
if (!control_pid_good(s))
service_enter_dead(s, f, true);
s->control_pid = 0;
if (s->control_command) {
f = SERVICE_SUCCESS;
if (f != SERVICE_SUCCESS)
s->result = f;
if (s->control_command &&
f == SERVICE_SUCCESS) {
switch (s->state) {
case SERVICE_START_PRE:
if (f == SERVICE_SUCCESS)
case SERVICE_START:
if (f != SERVICE_SUCCESS) {
if (s->pid_file) {
bool has_start_post;
if (!has_start_post && r < 0) {
r = service_demand_pid_file(s);
if (r < 0 || !cgroup_good(s))
case SERVICE_START_POST:
if (f != SERVICE_SUCCESS) {
service_enter_stop(s, f);
if (s->pid_file) {
r = service_load_pid_file(s, true);
r = service_demand_pid_file(s);
if (r < 0 || !cgroup_good(s))
case SERVICE_RELOAD:
if (f == SERVICE_SUCCESS) {
service_load_pid_file(s, true);
s->reload_result = f;
case SERVICE_STOP:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
if (main_pid_good(s) <= 0)
service_enter_stop_post(s, f);
case SERVICE_STOP_POST:
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
if (main_pid_good(s) <= 0)
service_enter_dead(s, f, true);
assert(s);
switch (s->state) {
case SERVICE_START_PRE:
case SERVICE_START:
case SERVICE_START_POST:
case SERVICE_RELOAD:
case SERVICE_STOP:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
case SERVICE_STOP_POST:
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
case SERVICE_AUTO_RESTART:
assert(s);
assert(u);
switch (s->state) {
case SERVICE_START:
case SERVICE_START_POST:
if (s->pid_file_pathspec) {
case SERVICE_RUNNING:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
case SERVICE_STOP_POST:
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
bool notify_dbus = false;
assert(u);
notify_dbus = true;
notify_dbus = true;
log_oom();
t = NULL;
s->status_text = t;
notify_dbus = true;
free(t);
if (notify_dbus)
#ifdef HAVE_SYSV_COMPAT
Iterator j;
assert(m);
if (!path) {
r = -ENOMEM;
goto finish;
closedir(d);
if (!fpath) {
r = -ENOMEM;
goto finish;
if (!name) {
r = log_oom();
goto finish;
goto finish;
goto finish;
goto finish;
goto finish;
goto finish;
goto finish;
static void service_bus_name_owner_change(
Unit *u,
const char *name,
const char *old_owner,
const char *new_owner) {
assert(s);
else if (old_owner)
} else if (new_owner &&
s->main_pid <= 0 &&
log_debug_unit(u->id, "%s's D-Bus name %s is now owned by process %u", u->id, name, (unsigned) pid);
assert(s);
return -EINVAL;
if (s->socket_fd >= 0)
return -EBUSY;
return -EAGAIN;
_cleanup_free_ char *a;
return -ENOMEM;
assert(s);
.sections =
#ifdef HAVE_SYSV_COMPAT
.can_transient = true,
.starting_stopping = {
.finished_start_job = {
.finished_stop_job = {