unit.c revision 0a6f50c0afdfc434b492493bd9efab20cbee8623
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering This file is part of systemd.
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering Copyright 2010 Lennart Poettering
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering systemd is free software; you can redistribute it and/or modify it
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering under the terms of the GNU Lesser General Public License as published by
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering (at your option) any later version.
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering systemd is distributed in the hope that it will be useful, but
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering Lesser General Public License for more details.
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering You should have received a copy of the GNU Lesser General Public License
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
#include "load-fragment.h"
#include "load-dropin.h"
#include "log.h"
#include "unit-name.h"
#include "dbus-unit.h"
#include "special.h"
#include "cgroup-util.h"
#include "missing.h"
#include "mkdir.h"
#include "label.h"
#include "fileio-label.h"
#include "bus-common-errors.h"
#include "dbus.h"
#include "execute.h"
#include "virt.h"
#include "dropin.h"
static int maybe_warn_about_dependency(const char *id, const char *other, UnitDependency dependency);
Unit *u;
assert(m);
return NULL;
if (!u->names) {
free(u);
return NULL;
u->manager = m;
u->default_dependencies = true;
assert(u);
assert(u);
if (cc) {
if (ec)
if (kc)
UnitType t;
assert(u);
if (!u->instance)
return -EINVAL;
return -ENOMEM;
return -EINVAL;
return -EINVAL;
r = unit_name_to_instance(s, &i);
return -EINVAL;
return -EINVAL;
return -EEXIST;
return -E2BIG;
if (r == -EEXIST)
u->type = t;
u->id = s;
u->instance = i;
unit_init(u);
i = NULL;
s = NULL;
assert(u);
if (!u->instance)
return -EINVAL;
return -ENOMEM;
name = t;
return -ENOENT;
r = unit_name_to_instance(s, &i);
u->id = s;
u->instance = i;
assert(u);
s = NULL;
return -ENOMEM;
u->description = s;
assert(u);
if (u->job)
if (u->nop_job)
if (u->no_gc)
if (u->refs)
assert(u);
u->in_load_queue = true;
assert(u);
if (u->in_cleanup_queue)
u->in_cleanup_queue = true;
assert(u);
if (unit_check_gc(u))
u->in_gc_queue = true;
assert(u);
u->sent_dbus_new_signal = true;
u->in_dbus_queue = true;
Iterator i;
assert(u);
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
set_free(s);
assert(u);
if (!u->transient)
if (u->fragment_path)
unlink(*i);
r = path_get_parent(*i, &p);
rmdir(p);
PATH_FOREACH_PREFIX_MORE(s, *j) {
Set *x;
set_remove(x, u);
if (set_isempty(x)) {
free(y);
set_free(x);
assert(u);
if (u->type < 0)
if (ec)
if (cc)
Iterator i;
assert(u);
unit_done(u);
if (u->job) {
job_uninstall(j);
job_free(j);
if (u->nop_job) {
job_uninstall(j);
job_free(j);
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
if (u->in_load_queue)
if (u->in_dbus_queue)
if (u->in_cleanup_queue)
if (u->in_gc_queue) {
if (u->in_cgroup_queue)
if (u->cgroup_path) {
while (u->refs)
free(u);
assert(u);
assert(u);
assert(s);
if (!*other)
*s = *other;
Iterator i;
assert(u);
unsigned n_reserve;
assert(u);
if (!u->dependencies[d])
Iterator i;
assert(u);
for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) {
if (back == u) {
if (r == -EEXIST)
if (back)
assert(u);
if (other == u)
return -EINVAL;
return -EINVAL;
return -EEXIST;
return -EEXIST;
return -EEXIST;
return -EEXIST;
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
assert(u);
if (!u->instance)
return -EINVAL;
return -ENOMEM;
name = s;
if (!other)
assert(u);
assert(u);
assert(c);
if (c->working_directory) {
if (c->root_directory) {
if (c->private_tmp) {
assert(u);
if (u->description)
return u->description;
Iterator i;
const char *prefix2;
assert(u);
fprintf(f,
prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->inactive_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)),
if (following)
if (u->fragment_path)
if (u->source_path)
if (u->job_timeout > 0)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
fprintf(f, "%s\tJob Timeout Action: %s\n", prefix, failure_action_to_string(u->job_timeout_action));
if (u->job_timeout_reboot_arg)
fprintf(f,
fprintf(f,
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
fprintf(f,
fprintf(f,
fprintf(f,
if (u->job)
if (u->nop_job)
assert(u);
r = unit_load_fragment(u);
return -ENOENT;
assert(u);
r = unit_load_fragment(u);
assert(u);
if (!u->default_dependencies ||
Iterator i;
assert(u);
assert(u);
if (!unit_get_cgroup_context(u))
return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true);
assert(u);
Unit *m;
if (m->fragment_path) {
CGroupContext *c;
c = unit_get_cgroup_context(u);
if (r == -EEXIST)
assert(u);
if (u->in_load_queue) {
u->in_load_queue = false;
return -EINVAL;
goto fail;
r = -ENOENT;
goto fail;
r = unit_add_target_dependencies(u);
goto fail;
r = unit_add_slice_dependencies(u);
goto fail;
r = unit_add_mount_dependencies(u);
goto fail;
r = unit_add_startup_units(u);
goto fail;
log_unit_error(u->id, "More than one OnFailure= dependencies specified for %s but OnFailureJobMode=isolate set. Refusing.", u->id);
r = -EINVAL;
goto fail;
fail:
u->load_error = r;
static bool unit_condition_test_list(Unit *u, Condition *first, const char *(*to_string)(ConditionType t)) {
Condition *c;
assert(u);
if (!first)
r = condition_test(c);
c->parameter,
u->id,
strerror(-r));
c->parameter,
u->id);
if (!c->trigger && r <= 0)
triggered = r > 0;
return triggered != 0;
assert(u);
return u->condition_result;
assert(u);
return u->assert_result;
assert(u);
assert(t >= 0);
return NULL;
if (!format_table)
return NULL;
const char *format;
assert(u);
assert(t >= 0);
if (format)
return format;
if (t == JOB_START)
else if (t == JOB_STOP)
else if (t == JOB_RELOAD)
return NULL;
const char *format;
assert(u);
if (!format)
const char *format;
assert(u);
if (log_on_console())
if (!format)
NULL);
assert(u);
return -EINVAL;
return -EALREADY;
!unit_condition_test(u)) {
return -EALREADY;
!unit_assert_test(u)) {
return -EPROTO;
if (following) {
return -ENOTSUP;
return -EBADR;
assert(u);
assert(u);
return unit_can_start(u) &&
u->allow_isolate;
assert(u);
return -EALREADY;
if (following) {
return -EBADR;
assert(u);
return -EINVAL;
if (!unit_can_reload(u))
return -EBADR;
return -EALREADY;
return -ENOEXEC;
if (following) {
assert(u);
Iterator i;
assert(u);
if (!u->stop_when_unneeded)
bool stop = false;
Iterator i;
assert(u);
if (u->job)
stop = true;
if (!stop)
Iterator i;
assert(u);
Iterator i;
assert(u);
Iterator i;
assert(u);
Iterator i;
assert(u);
Iterator i;
assert(u);
Manager *m;
bool unexpected;
assert(u);
m = u->manager;
if (m->n_reloading <= 0) {
m->n_on_console --;
if (m->n_on_console == 0)
m->no_console_output = false;
m->n_on_console ++;
if (u->job) {
unexpected = false;
case JOB_START:
case JOB_VERIFY_ACTIVE:
unexpected = true;
case JOB_RELOAD:
case JOB_RELOAD_OR_START:
unexpected = true;
case JOB_STOP:
case JOB_RESTART:
case JOB_TRY_RESTART:
unexpected = true;
unexpected = true;
if (m->n_reloading <= 0) {
if (unexpected) {
bus_init(m, true);
m->n_reloading <= 0) {
u->in_audit = true;
manager_send_unit_plymouth(m, u);
m->n_reloading <= 0) {
if (!u->in_audit) {
u->in_audit = false;
assert(u);
if (r == -EEXIST) {
assert(u);
assert(u);
int ret = 0, r;
assert(u);
if (r < 0 && ret >= 0)
ret = r;
if (r < 0 && ret >= 0)
ret = r;
} else if (ret >= 0)
ret = r;
char *fn;
return -ENOMEM;
r = unit_watch_pids_in_path(u, p);
if (r < 0 && ret >= 0)
ret = r;
if (r < 0 && ret >= 0)
ret = r;
} else if (ret >= 0)
ret = r;
return ret;
assert(u);
if (!u->cgroup_path)
return -ENOENT;
Iterator i;
assert(u);
assert(u);
case JOB_VERIFY_ACTIVE:
case JOB_START:
case JOB_STOP:
case JOB_NOP:
case JOB_RESTART:
case JOB_TRY_RESTART:
return unit_can_start(u);
case JOB_RELOAD:
return unit_can_reload(u);
case JOB_RELOAD_OR_START:
static int maybe_warn_about_dependency(const char *id, const char *other, UnitDependency dependency) {
switch (dependency) {
case UNIT_REQUIRES:
case UNIT_WANTS:
case UNIT_REQUISITE:
case UNIT_BINDS_TO:
case UNIT_PART_OF:
case UNIT_REQUIRED_BY:
case UNIT_WANTED_BY:
case UNIT_BOUND_BY:
case UNIT_CONSISTS_OF:
case UNIT_REFERENCES:
case UNIT_REFERENCED_BY:
case UNIT_JOINS_NAMESPACE_OF:
case UNIT_CONFLICTS:
case UNIT_CONFLICTED_BY:
case UNIT_BEFORE:
case UNIT_AFTER:
case UNIT_ON_FAILURE:
case UNIT_TRIGGERS:
case UNIT_TRIGGERED_BY:
return -EINVAL;
case _UNIT_DEPENDENCY_MAX:
case _UNIT_DEPENDENCY_INVALID:
assert(u);
u = unit_follow_merge(u);
if (u == other) {
if (add_reference) {
goto fail;
if (add_reference) {
goto fail;
goto fail;
fail:
int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference) {
assert(u);
assert(u);
assert(p);
if (!name)
*p = NULL;
return name;
if (u->instance)
return NULL;
return NULL;
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
assert(u);
if (!name)
return -ENOMEM;
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
assert(u);
if (!name)
return -ENOMEM;
int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
assert(u);
if (!name)
return -ENOMEM;
int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
assert(u);
if (!name)
return -ENOMEM;
int set_unit_path(const char *p) {
return -errno;
assert(u);
if (!u->id)
return NULL;
assert(u);
return NULL;
if (!escaped)
return NULL;
if (slice)
const char *slice_name;
assert(u);
assert(c);
if (u->instance) {
if (!prefix)
return -ENOMEM;
if (!escaped)
return -ENOMEM;
return -ENOMEM;
slice_name = b;
assert(u);
return NULL;
assert(u);
return -ENOMEM;
assert(u);
assert(u);
assert(u);
assert(u);
assert(f);
if (unit_can_serialize(u)) {
if (rt) {
if (u->cgroup_path)
if (serialize_jobs) {
if (u->job) {
if (u->nop_job) {
assert(u);
assert(f);
assert(u);
assert(f);
assert(u);
assert(f);
if (offset > 0)
size_t k;
if (feof(f))
return -errno;
Job *j;
j = job_new_raw(u);
return -ENOMEM;
job_free(j);
job_free(j);
r = job_install_deserialized(j);
job_free(j);
if (type < 0)
b = parse_boolean(v);
u->condition_result = b;
b = parse_boolean(v);
u->assert_result = b;
b = parse_boolean(v);
u->transient = b;
s = strdup(v);
return -ENOMEM;
if (u->cgroup_path) {
u->cgroup_path, p);
u->cgroup_path = s;
if (unit_can_serialize(u)) {
if (rt) {
assert(u);
if (!what)
return -ENOMEM;
if (wants) {
assert(u);
if (u->job) {
} else if (u->deserialized_job >= 0) {
r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL);
char **path;
assert(u);
if (u->fragment_path) {
if (u->fragment_mtime > 0 &&
if (u->source_path) {
if (u->source_mtime > 0 &&
(void) unit_find_dropin_paths(u, &t);
if (loaded_cnt == 0)
if (u->dropin_mtime > 0 &&
assert(u);
assert(u);
return NULL;
assert(u);
assert(u);
if (unit_stop_pending(u))
assert(u);
if (u->job &&
assert(u);
return -ENOTSUP;
if (!pid_set)
return NULL;
if (main_pid > 0) {
goto fail;
if (control_pid > 0) {
goto fail;
return pid_set;
fail:
return NULL;
int unit_kill_common(
Unit *u,
int signo,
if (main_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
if (control_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type));
if (control_pid > 0)
r = -errno;
if (main_pid > 0)
r = -errno;
if (!pid_set)
return -ENOMEM;
q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
assert(u);
assert(s);
*s = NULL;
assert(u);
return u->unit_file_state;
assert(u);
return u->unit_file_preset;
assert(u);
assert(u);
if (ec) {
for (i = 0; i < _RLIMIT_MAX; i++)
return -ENOMEM;
if (cc) {
if (ec &&
assert(u);
if (u->type < 0)
return NULL;
if (offset <= 0)
return NULL;
assert(u);
if (u->type < 0)
return NULL;
if (offset <= 0)
return NULL;
if (u->type < 0)
return NULL;
if (offset <= 0)
return NULL;
if (u->type < 0)
return NULL;
if (offset <= 0)
return NULL;
return -ENOENT;
if (!*dir)
return -ENOMEM;
assert(u);
assert(u);
int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) {
assert(u);
return -ENOMEM;
int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
assert(u);
return -EINVAL;
if (!ndata)
return -ENOMEM;
int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) {
assert(u);
return -ENOMEM;
assert(u);
if (unlink(q) < 0)
rmdir(p);
assert(u);
u->load_error = 0;
u->transient = true;
r = user_runtime_dir(&c);
return -ENOENT;
if (!u->fragment_path)
return -ENOMEM;
if (!u->fragment_path)
return -ENOMEM;
int unit_kill_context(
Unit *u,
KillContext *c,
bool main_pid_alien) {
assert(u);
assert(c);
case KILL_KILL:
case KILL_ABORT:
case KILL_TERMINATE:
if (main_pid > 0) {
if (r < 0 && r != -ESRCH) {
log_unit_warning_errno(u->id, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm));
if (!main_pid_alien)
wait_for_exit = true;
if (control_pid > 0) {
if (r < 0 && r != -ESRCH) {
log_unit_warning_errno(u->id, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm));
wait_for_exit = true;
if ((c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL)) && u->cgroup_path) {
if (!pid_set)
return -ENOMEM;
if (!pid_set)
return -ENOMEM;
return wait_for_exit;
assert(u);
return -EINVAL;
return -ENOMEM;
if (!path_is_safe(p)) {
free(p);
return -EPERM;
free(p);
Set *x;
return -ENOMEM;
return -ENOMEM;
free(q);
return -ENOMEM;
free(q);
set_free(x);
r = set_put(x, u);
Iterator i;
if (*rt)
if (*rt) {