Systemd/src/core/unit.c

2856 lines
82 KiB
C
Raw Normal View History

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-01-26 21:39:06 +01:00
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
2010-01-26 21:39:06 +01:00
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sys/poll.h>
2010-01-27 00:15:56 +01:00
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
2010-01-26 21:39:06 +01:00
#include "set.h"
#include "unit.h"
#include "macro.h"
#include "strv.h"
2012-05-07 21:36:12 +02:00
#include "path-util.h"
2010-01-26 21:39:06 +01:00
#include "load-fragment.h"
#include "load-dropin.h"
#include "log.h"
2010-04-15 03:11:11 +02:00
#include "unit-name.h"
#include "specifier.h"
2010-04-18 03:08:16 +02:00
#include "dbus-unit.h"
2010-06-18 04:22:59 +02:00
#include "special.h"
#include "cgroup-util.h"
#include "missing.h"
#include "cgroup-attr.h"
2010-01-26 21:39:06 +01:00
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
[UNIT_TIMER] = &timer_vtable,
[UNIT_SOCKET] = &socket_vtable,
[UNIT_TARGET] = &target_vtable,
[UNIT_DEVICE] = &device_vtable,
[UNIT_MOUNT] = &mount_vtable,
[UNIT_AUTOMOUNT] = &automount_vtable,
2010-05-09 18:44:11 +02:00
[UNIT_SNAPSHOT] = &snapshot_vtable,
[UNIT_SWAP] = &swap_vtable,
[UNIT_PATH] = &path_vtable
2010-01-26 21:39:06 +01:00
};
Unit *unit_new(Manager *m, size_t size) {
2010-01-26 21:39:06 +01:00
Unit *u;
assert(m);
assert(size >= sizeof(Unit));
2010-01-26 21:39:06 +01:00
u = malloc0(size);
if (!u)
2010-01-26 21:39:06 +01:00
return NULL;
u->names = set_new(string_hash_func, string_compare_func);
if (!u->names) {
2010-01-26 21:39:06 +01:00
free(u);
return NULL;
}
u->manager = m;
u->type = _UNIT_TYPE_INVALID;
u->deserialized_job = _JOB_TYPE_INVALID;
u->default_dependencies = true;
u->unit_file_state = _UNIT_FILE_STATE_INVALID;
2010-01-26 21:39:06 +01:00
return u;
}
bool unit_has_name(Unit *u, const char *name) {
assert(u);
assert(name);
return !!set_get(u->names, (char*) name);
}
2010-01-26 21:39:06 +01:00
int unit_add_name(Unit *u, const char *text) {
UnitType t;
char *s, *i = NULL;
2010-01-26 21:39:06 +01:00
int r;
assert(u);
assert(text);
2010-04-15 03:11:11 +02:00
if (unit_name_is_template(text)) {
if (!u->instance)
2010-04-15 03:11:11 +02:00
return -EINVAL;
2010-01-26 21:39:06 +01:00
s = unit_name_replace_instance(text, u->instance);
2010-04-15 03:11:11 +02:00
} else
s = strdup(text);
2010-01-26 21:39:06 +01:00
2010-04-15 03:11:11 +02:00
if (!s)
return -ENOMEM;
2010-01-26 21:39:06 +01:00
if (!unit_name_is_valid(s, false)) {
2010-04-15 03:11:11 +02:00
r = -EINVAL;
goto fail;
}
2010-04-15 03:11:11 +02:00
assert_se((t = unit_name_to_type(s)) >= 0);
2010-01-26 21:39:06 +01:00
if (u->type != _UNIT_TYPE_INVALID && t != u->type) {
2010-04-15 03:11:11 +02:00
r = -EINVAL;
goto fail;
}
2010-01-26 21:39:06 +01:00
2010-04-15 03:11:11 +02:00
if ((r = unit_name_to_instance(s, &i)) < 0)
goto fail;
2010-01-26 21:39:06 +01:00
if (i && unit_vtable[t]->no_instances) {
r = -EINVAL;
2010-04-15 03:11:11 +02:00
goto fail;
}
2010-04-15 03:11:11 +02:00
/* Ensure that this unit is either instanced or not instanced,
* but not both. */
if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) {
2010-04-15 03:11:11 +02:00
r = -EINVAL;
goto fail;
}
if (unit_vtable[t]->no_alias &&
!set_isempty(u->names) &&
!set_get(u->names, s)) {
2010-04-15 03:11:11 +02:00
r = -EEXIST;
goto fail;
}
if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) {
r = -E2BIG;
goto fail;
}
if ((r = set_put(u->names, s)) < 0) {
2010-04-15 03:11:11 +02:00
if (r == -EEXIST)
r = 0;
goto fail;
2010-01-26 21:39:06 +01:00
}
if ((r = hashmap_put(u->manager->units, s, u)) < 0) {
set_remove(u->names, s);
2010-04-15 03:11:11 +02:00
goto fail;
2010-01-26 21:39:06 +01:00
}
if (u->type == _UNIT_TYPE_INVALID) {
2010-01-29 06:04:08 +01:00
u->type = t;
u->id = s;
u->instance = i;
2010-04-15 03:11:11 +02:00
LIST_PREPEND(Unit, units_by_type, u->manager->units_by_type[t], u);
if (UNIT_VTABLE(u)->init)
UNIT_VTABLE(u)->init(u);
2010-04-15 03:11:11 +02:00
} else
free(i);
2010-01-26 21:39:06 +01:00
unit_add_to_dbus_queue(u);
2010-01-26 21:39:06 +01:00
return 0;
2010-04-15 03:11:11 +02:00
fail:
free(s);
free(i);
return r;
2010-01-26 21:39:06 +01:00
}
int unit_choose_id(Unit *u, const char *name) {
char *s, *t = NULL, *i;
int r;
assert(u);
assert(name);
2010-04-15 03:11:11 +02:00
if (unit_name_is_template(name)) {
if (!u->instance)
2010-04-15 03:11:11 +02:00
return -EINVAL;
if (!(t = unit_name_replace_instance(name, u->instance)))
2010-04-15 03:11:11 +02:00
return -ENOMEM;
name = t;
}
/* Selects one of the names of this unit as the id */
s = set_get(u->names, (char*) name);
2010-04-15 03:11:11 +02:00
free(t);
2010-04-15 03:11:11 +02:00
if (!s)
return -ENOENT;
if ((r = unit_name_to_instance(s, &i)) < 0)
return r;
u->id = s;
free(u->instance);
u->instance = i;
unit_add_to_dbus_queue(u);
2010-04-15 03:11:11 +02:00
return 0;
}
2010-01-29 03:18:09 +01:00
int unit_set_description(Unit *u, const char *description) {
char *s;
assert(u);
if (!(s = strdup(description)))
return -ENOMEM;
free(u->description);
u->description = s;
unit_add_to_dbus_queue(u);
2010-01-29 03:18:09 +01:00
return 0;
}
bool unit_check_gc(Unit *u) {
assert(u);
if (u->load_state == UNIT_STUB)
return true;
if (UNIT_VTABLE(u)->no_gc)
return true;
if (u->no_gc)
return true;
if (u->job)
return true;
core: add NOP jobs, job type collapsing Two of our current job types are special: JOB_TRY_RESTART, JOB_RELOAD_OR_START. They differ from other job types by being sensitive to the unit active state. They perform some action when the unit is active and some other action otherwise. This raises a question: when exactly should the unit state be checked to make the decision? Currently the unit state is checked when the job becomes runnable. It's more sensible to check the state immediately when the job is added by the user. When the user types "systemctl try-restart foo.service", he really intends to restart the service if it's running right now. If it isn't running right now, the restart is pointless. Consider the example (from Bugzilla[1]): sleep.service takes some time to start. hello.service has After=sleep.service. Both services get started. Two jobs will appear: hello.service/start waiting sleep.service/start running Then someone runs "systemctl try-restart hello.service". Currently the try-restart operation will block and wait for sleep.service/start to complete. The correct result is to complete the try-restart operation immediately with success, because hello.service is not running. The two original jobs must not be disturbed by this. To fix this we introduce two new concepts: - a new job type: JOB_NOP A JOB_NOP job does not do anything to the unit. It does not pull in any dependencies. It is always immediately runnable. When installed to a unit, it sits in a special slot (u->nop_job) where it never conflicts with the installed job (u->job) of a different type. It never merges with jobs of other types, but it can merge into an already installed JOB_NOP job. - "collapsing" of job types When a job of one of the two special types is added, the state of the unit is checked immediately and the job type changes: JOB_TRY_RESTART -> JOB_RESTART or JOB_NOP JOB_RELOAD_OR_START -> JOB_RELOAD or JOB_START Should a job type JOB_RELOAD_OR_START appear later during job merging, it collapses immediately afterwards. Collapsing actually makes some things simpler, because there are now fewer job types that are allowed in the transaction. [1] Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=753586
2012-04-25 11:58:27 +02:00
if (u->nop_job)
return true;
if (unit_active_state(u) != UNIT_INACTIVE)
return true;
if (UNIT_VTABLE(u)->check_gc)
if (UNIT_VTABLE(u)->check_gc(u))
return true;
return false;
}
2010-01-26 21:39:06 +01:00
void unit_add_to_load_queue(Unit *u) {
assert(u);
assert(u->type != _UNIT_TYPE_INVALID);
2010-01-26 21:39:06 +01:00
if (u->load_state != UNIT_STUB || u->in_load_queue)
2010-01-26 21:39:06 +01:00
return;
LIST_PREPEND(Unit, load_queue, u->manager->load_queue, u);
u->in_load_queue = true;
2010-01-26 21:39:06 +01:00
}
2010-04-06 02:43:58 +02:00
void unit_add_to_cleanup_queue(Unit *u) {
assert(u);
if (u->in_cleanup_queue)
2010-04-06 02:43:58 +02:00
return;
LIST_PREPEND(Unit, cleanup_queue, u->manager->cleanup_queue, u);
u->in_cleanup_queue = true;
2010-04-06 02:43:58 +02:00
}
void unit_add_to_gc_queue(Unit *u) {
assert(u);
if (u->in_gc_queue || u->in_cleanup_queue)
return;
if (unit_check_gc(u))
return;
LIST_PREPEND(Unit, gc_queue, u->manager->gc_queue, u);
u->in_gc_queue = true;
u->manager->n_in_gc_queue ++;
if (u->manager->gc_queue_timestamp <= 0)
u->manager->gc_queue_timestamp = now(CLOCK_MONOTONIC);
}
void unit_add_to_dbus_queue(Unit *u) {
assert(u);
assert(u->type != _UNIT_TYPE_INVALID);
if (u->load_state == UNIT_STUB || u->in_dbus_queue)
return;
/* Shortcut things if nobody cares */
if (!bus_has_subscriber(u->manager)) {
u->sent_dbus_new_signal = true;
return;
}
LIST_PREPEND(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
u->in_dbus_queue = true;
}
2010-01-26 21:39:06 +01:00
static void bidi_set_free(Unit *u, Set *s) {
Iterator i;
Unit *other;
assert(u);
/* Frees the set and makes sure we are dropped from the
* inverse pointers */
SET_FOREACH(other, s, i) {
UnitDependency d;
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
set_remove(other->dependencies[d], u);
unit_add_to_gc_queue(other);
2010-01-26 21:39:06 +01:00
}
set_free(s);
}
void unit_free(Unit *u) {
UnitDependency d;
Iterator i;
char *t;
assert(u);
bus_unit_send_removed_signal(u);
if (u->load_state != UNIT_STUB)
2010-06-04 22:31:33 +02:00
if (UNIT_VTABLE(u)->done)
UNIT_VTABLE(u)->done(u);
SET_FOREACH(t, u->names, i)
hashmap_remove_value(u->manager->units, t, u);
2010-01-26 21:39:06 +01:00
if (u->job) {
Job *j = u->job;
job_uninstall(j);
job_free(j);
}
core: add NOP jobs, job type collapsing Two of our current job types are special: JOB_TRY_RESTART, JOB_RELOAD_OR_START. They differ from other job types by being sensitive to the unit active state. They perform some action when the unit is active and some other action otherwise. This raises a question: when exactly should the unit state be checked to make the decision? Currently the unit state is checked when the job becomes runnable. It's more sensible to check the state immediately when the job is added by the user. When the user types "systemctl try-restart foo.service", he really intends to restart the service if it's running right now. If it isn't running right now, the restart is pointless. Consider the example (from Bugzilla[1]): sleep.service takes some time to start. hello.service has After=sleep.service. Both services get started. Two jobs will appear: hello.service/start waiting sleep.service/start running Then someone runs "systemctl try-restart hello.service". Currently the try-restart operation will block and wait for sleep.service/start to complete. The correct result is to complete the try-restart operation immediately with success, because hello.service is not running. The two original jobs must not be disturbed by this. To fix this we introduce two new concepts: - a new job type: JOB_NOP A JOB_NOP job does not do anything to the unit. It does not pull in any dependencies. It is always immediately runnable. When installed to a unit, it sits in a special slot (u->nop_job) where it never conflicts with the installed job (u->job) of a different type. It never merges with jobs of other types, but it can merge into an already installed JOB_NOP job. - "collapsing" of job types When a job of one of the two special types is added, the state of the unit is checked immediately and the job type changes: JOB_TRY_RESTART -> JOB_RESTART or JOB_NOP JOB_RELOAD_OR_START -> JOB_RELOAD or JOB_START Should a job type JOB_RELOAD_OR_START appear later during job merging, it collapses immediately afterwards. Collapsing actually makes some things simpler, because there are now fewer job types that are allowed in the transaction. [1] Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=753586
2012-04-25 11:58:27 +02:00
if (u->nop_job) {
Job *j = u->nop_job;
job_uninstall(j);
job_free(j);
}
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
bidi_set_free(u, u->dependencies[d]);
if (u->requires_mounts_for) {
LIST_REMOVE(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
strv_free(u->requires_mounts_for);
}
if (u->type != _UNIT_TYPE_INVALID)
LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u);
2010-01-29 06:04:08 +01:00
if (u->in_load_queue)
LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u);
2010-01-26 21:39:06 +01:00
if (u->in_dbus_queue)
LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
if (u->in_cleanup_queue)
LIST_REMOVE(Unit, cleanup_queue, u->manager->cleanup_queue, u);
2010-04-06 02:43:58 +02:00
if (u->in_gc_queue) {
LIST_REMOVE(Unit, gc_queue, u->manager->gc_queue, u);
u->manager->n_in_gc_queue--;
}
cgroup_bonding_free_list(u->cgroup_bondings, u->manager->n_reloading <= 0);
cgroup_attribute_free_list(u->cgroup_attributes);
2010-01-26 21:39:06 +01:00
free(u->description);
strv_free(u->documentation);
free(u->fragment_path);
free(u->source_path);
free(u->instance);
2010-01-26 21:39:06 +01:00
set_free_free(u->names);
2010-01-26 21:39:06 +01:00
condition_free_list(u->conditions);
while (u->refs)
unit_ref_unset(u->refs);
2010-01-26 21:39:06 +01:00
free(u);
}
UnitActiveState unit_active_state(Unit *u) {
assert(u);
if (u->load_state == UNIT_MERGED)
return unit_active_state(unit_follow_merge(u));
/* After a reload it might happen that a unit is not correctly
* loaded but still has a process around. That's why we won't
* shortcut failed loading to UNIT_INACTIVE_FAILED. */
2010-01-26 21:39:06 +01:00
return UNIT_VTABLE(u)->active_state(u);
}
const char* unit_sub_state_to_string(Unit *u) {
assert(u);
return UNIT_VTABLE(u)->sub_state_to_string(u);
}
2010-04-06 02:43:58 +02:00
static void complete_move(Set **s, Set **other) {
assert(s);
assert(other);
2010-01-26 21:39:06 +01:00
2010-04-06 02:43:58 +02:00
if (!*other)
return;
2010-01-26 21:39:06 +01:00
if (*s)
2010-04-06 02:43:58 +02:00
set_move(*s, *other);
else {
*s = *other;
*other = NULL;
}
}
2010-01-26 21:39:06 +01:00
2010-04-06 02:43:58 +02:00
static void merge_names(Unit *u, Unit *other) {
char *t;
Iterator i;
2010-01-26 21:39:06 +01:00
2010-04-06 02:43:58 +02:00
assert(u);
assert(other);
complete_move(&u->names, &other->names);
2010-04-06 02:43:58 +02:00
set_free_free(other->names);
other->names = NULL;
other->id = NULL;
2010-04-06 02:43:58 +02:00
SET_FOREACH(t, u->names, i)
assert_se(hashmap_replace(u->manager->units, t, u) == 0);
2010-01-26 21:39:06 +01:00
}
2010-04-06 02:43:58 +02:00
static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) {
Iterator i;
Unit *back;
2010-01-26 21:39:06 +01:00
int r;
2010-04-06 02:43:58 +02:00
assert(u);
assert(other);
assert(d < _UNIT_DEPENDENCY_MAX);
/* Fix backwards pointers */
SET_FOREACH(back, other->dependencies[d], i) {
2010-04-06 02:43:58 +02:00
UnitDependency k;
for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++)
if ((r = set_remove_and_put(back->dependencies[k], other, u)) < 0) {
2010-04-06 02:43:58 +02:00
if (r == -EEXIST)
set_remove(back->dependencies[k], other);
2010-04-06 02:43:58 +02:00
else
assert(r == -ENOENT);
}
}
complete_move(&u->dependencies[d], &other->dependencies[d]);
2010-04-06 02:43:58 +02:00
set_free(other->dependencies[d]);
other->dependencies[d] = NULL;
2010-04-06 02:43:58 +02:00
}
int unit_merge(Unit *u, Unit *other) {
2010-01-26 21:39:06 +01:00
UnitDependency d;
assert(u);
assert(other);
assert(u->manager == other->manager);
assert(u->type != _UNIT_TYPE_INVALID);
2010-01-26 21:39:06 +01:00
other = unit_follow_merge(other);
2010-04-06 02:43:58 +02:00
if (other == u)
return 0;
if (u->type != other->type)
2010-04-15 03:11:11 +02:00
return -EINVAL;
if (!u->instance != !other->instance)
2010-01-26 21:39:06 +01:00
return -EINVAL;
if (other->load_state != UNIT_STUB &&
other->load_state != UNIT_ERROR)
2010-04-06 02:43:58 +02:00
return -EEXIST;
2010-01-26 21:39:06 +01:00
if (other->job)
return -EEXIST;
core: add NOP jobs, job type collapsing Two of our current job types are special: JOB_TRY_RESTART, JOB_RELOAD_OR_START. They differ from other job types by being sensitive to the unit active state. They perform some action when the unit is active and some other action otherwise. This raises a question: when exactly should the unit state be checked to make the decision? Currently the unit state is checked when the job becomes runnable. It's more sensible to check the state immediately when the job is added by the user. When the user types "systemctl try-restart foo.service", he really intends to restart the service if it's running right now. If it isn't running right now, the restart is pointless. Consider the example (from Bugzilla[1]): sleep.service takes some time to start. hello.service has After=sleep.service. Both services get started. Two jobs will appear: hello.service/start waiting sleep.service/start running Then someone runs "systemctl try-restart hello.service". Currently the try-restart operation will block and wait for sleep.service/start to complete. The correct result is to complete the try-restart operation immediately with success, because hello.service is not running. The two original jobs must not be disturbed by this. To fix this we introduce two new concepts: - a new job type: JOB_NOP A JOB_NOP job does not do anything to the unit. It does not pull in any dependencies. It is always immediately runnable. When installed to a unit, it sits in a special slot (u->nop_job) where it never conflicts with the installed job (u->job) of a different type. It never merges with jobs of other types, but it can merge into an already installed JOB_NOP job. - "collapsing" of job types When a job of one of the two special types is added, the state of the unit is checked immediately and the job type changes: JOB_TRY_RESTART -> JOB_RESTART or JOB_NOP JOB_RELOAD_OR_START -> JOB_RELOAD or JOB_START Should a job type JOB_RELOAD_OR_START appear later during job merging, it collapses immediately afterwards. Collapsing actually makes some things simpler, because there are now fewer job types that are allowed in the transaction. [1] Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=753586
2012-04-25 11:58:27 +02:00
if (other->nop_job)
return -EEXIST;
if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
return -EEXIST;
2010-01-26 21:39:06 +01:00
/* Merge names */
2010-04-06 02:43:58 +02:00
merge_names(u, other);
2010-01-26 21:39:06 +01:00
/* Redirect all references */
while (other->refs)
unit_ref_set(other->refs, u);
2010-01-26 21:39:06 +01:00
/* Merge dependencies */
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
2010-04-06 02:43:58 +02:00
merge_dependencies(u, other, d);
2010-01-26 21:39:06 +01:00
other->load_state = UNIT_MERGED;
other->merged_into = u;
2010-04-06 02:43:58 +02:00
/* If there is still some data attached to the other node, we
* don't need it anymore, and can free it. */
if (other->load_state != UNIT_STUB)
if (UNIT_VTABLE(other)->done)
UNIT_VTABLE(other)->done(other);
unit_add_to_dbus_queue(u);
2010-04-06 02:43:58 +02:00
unit_add_to_cleanup_queue(other);
return 0;
}
int unit_merge_by_name(Unit *u, const char *name) {
Unit *other;
2010-04-15 03:11:11 +02:00
int r;
char *s = NULL;
2010-04-06 02:43:58 +02:00
assert(u);
assert(name);
2010-04-15 03:11:11 +02:00
if (unit_name_is_template(name)) {
if (!u->instance)
2010-04-15 03:11:11 +02:00
return -EINVAL;
if (!(s = unit_name_replace_instance(name, u->instance)))
2010-04-15 03:11:11 +02:00
return -ENOMEM;
name = s;
}
if (!(other = manager_get_unit(u->manager, name)))
2010-04-15 03:11:11 +02:00
r = unit_add_name(u, name);
else
r = unit_merge(u, other);
2010-04-06 02:43:58 +02:00
2010-04-15 03:11:11 +02:00
free(s);
return r;
2010-04-06 02:43:58 +02:00
}
Unit* unit_follow_merge(Unit *u) {
assert(u);
while (u->load_state == UNIT_MERGED)
assert_se(u = u->merged_into);
2010-04-06 02:43:58 +02:00
return u;
}
int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
int r;
assert(u);
assert(c);
if (c->std_output != EXEC_OUTPUT_KMSG &&
c->std_output != EXEC_OUTPUT_SYSLOG &&
c->std_output != EXEC_OUTPUT_JOURNAL &&
c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE &&
c->std_output != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
c->std_error != EXEC_OUTPUT_KMSG &&
c->std_error != EXEC_OUTPUT_SYSLOG &&
c->std_error != EXEC_OUTPUT_JOURNAL &&
c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
c->std_error != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE)
2010-04-06 02:43:58 +02:00
return 0;
/* If syslog or kernel logging is requested, make sure our own
* logging daemon is run first. */
if (u->manager->running_as == MANAGER_SYSTEM)
if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0)
2010-04-06 02:43:58 +02:00
return r;
2010-01-26 21:39:06 +01:00
return 0;
}
const char *unit_description(Unit *u) {
assert(u);
if (u->description)
return u->description;
2010-01-26 21:39:06 +01:00
return strna(u->id);
2010-01-26 21:39:06 +01:00
}
void unit_dump(Unit *u, FILE *f, const char *prefix) {
char *t, **j;
2010-01-26 21:39:06 +01:00
UnitDependency d;
Iterator i;
2010-02-03 14:21:48 +01:00
char *p2;
const char *prefix2;
char
timestamp1[FORMAT_TIMESTAMP_MAX],
timestamp2[FORMAT_TIMESTAMP_MAX],
timestamp3[FORMAT_TIMESTAMP_MAX],
timestamp4[FORMAT_TIMESTAMP_MAX],
timespan[FORMAT_TIMESPAN_MAX];
Unit *following;
2010-01-26 21:39:06 +01:00
assert(u);
assert(u->type >= 0);
2010-01-26 21:39:06 +01:00
if (!prefix)
prefix = "";
2010-02-03 14:21:48 +01:00
p2 = strappend(prefix, "\t");
prefix2 = p2 ? p2 : prefix;
2010-01-26 21:39:06 +01:00
fprintf(f,
"%s-> Unit %s:\n"
2010-01-26 21:39:06 +01:00
"%s\tDescription: %s\n"
2010-04-15 03:11:11 +02:00
"%s\tInstance: %s\n"
2010-01-26 21:39:06 +01:00
"%s\tUnit Load State: %s\n"
2010-04-10 04:43:57 +02:00
"%s\tUnit Active State: %s\n"
"%s\tInactive Exit Timestamp: %s\n"
2010-04-10 04:43:57 +02:00
"%s\tActive Enter Timestamp: %s\n"
"%s\tActive Exit Timestamp: %s\n"
"%s\tInactive Enter Timestamp: %s\n"
"%s\tGC Check Good: %s\n"
"%s\tNeed Daemon Reload: %s\n",
prefix, u->id,
2010-01-26 21:39:06 +01:00
prefix, unit_description(u),
prefix, strna(u->instance),
prefix, unit_load_state_to_string(u->load_state),
2010-04-10 04:43:57 +02:00
prefix, unit_active_state_to_string(unit_active_state(u)),
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(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)),
prefix, yes_no(unit_check_gc(u)),
prefix, yes_no(unit_need_daemon_reload(u)));
2010-01-27 00:15:56 +01:00
SET_FOREACH(t, u->names, i)
2010-01-26 21:39:06 +01:00
fprintf(f, "%s\tName: %s\n", prefix, t);
STRV_FOREACH(j, u->documentation)
fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
if ((following = unit_following(u)))
fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
if (u->fragment_path)
fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path);
2010-04-06 02:43:58 +02:00
if (u->source_path)
fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path);
if (u->job_timeout > 0)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout));
condition_dump_list(u->conditions, f, prefix);
if (dual_timestamp_is_set(&u->condition_timestamp))
2011-03-17 04:36:19 +01:00
fprintf(f,
"%s\tCondition Timestamp: %s\n"
"%s\tCondition Result: %s\n",
prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->condition_timestamp.realtime)),
prefix, yes_no(u->condition_result));
2011-03-17 04:36:19 +01:00
2010-01-26 21:39:06 +01:00
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
Unit *other;
SET_FOREACH(other, u->dependencies[d], i)
fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->id);
2010-01-26 21:39:06 +01:00
}
if (!strv_isempty(u->requires_mounts_for)) {
fprintf(f,
"%s\tRequiresMountsFor:", prefix);
STRV_FOREACH(j, u->requires_mounts_for)
fprintf(f, " %s", *j);
fputs("\n", f);
}
if (u->load_state == UNIT_LOADED) {
CGroupBonding *b;
CGroupAttribute *a;
fprintf(f,
"%s\tStopWhenUnneeded: %s\n"
"%s\tRefuseManualStart: %s\n"
"%s\tRefuseManualStop: %s\n"
2011-04-07 04:11:31 +02:00
"%s\tDefaultDependencies: %s\n"
"%s\tOnFailureIsolate: %s\n"
"%s\tIgnoreOnIsolate: %s\n"
"%s\tIgnoreOnSnapshot: %s\n",
prefix, yes_no(u->stop_when_unneeded),
prefix, yes_no(u->refuse_manual_start),
prefix, yes_no(u->refuse_manual_stop),
prefix, yes_no(u->default_dependencies),
prefix, yes_no(u->on_failure_isolate),
prefix, yes_no(u->ignore_on_isolate),
prefix, yes_no(u->ignore_on_snapshot));
LIST_FOREACH(by_unit, b, u->cgroup_bondings)
2010-04-06 02:43:58 +02:00
fprintf(f, "%s\tControlGroup: %s:%s\n",
prefix, b->controller, b->path);
2010-03-31 16:29:55 +02:00
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
char *v = NULL;
if (a->map_callback)
a->map_callback(a->controller, a->name, a->value, &v);
fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
prefix, a->controller, a->name, v ? v : a->value);
free(v);
}
2010-04-06 02:43:58 +02:00
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
} else if (u->load_state == UNIT_MERGED)
fprintf(f,
"%s\tMerged into: %s\n",
prefix, u->merged_into->id);
else if (u->load_state == UNIT_ERROR)
fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error));
2010-01-26 21:39:06 +01:00
if (u->job)
job_dump(u->job, f, prefix2);
2010-01-26 21:39:06 +01:00
core: add NOP jobs, job type collapsing Two of our current job types are special: JOB_TRY_RESTART, JOB_RELOAD_OR_START. They differ from other job types by being sensitive to the unit active state. They perform some action when the unit is active and some other action otherwise. This raises a question: when exactly should the unit state be checked to make the decision? Currently the unit state is checked when the job becomes runnable. It's more sensible to check the state immediately when the job is added by the user. When the user types "systemctl try-restart foo.service", he really intends to restart the service if it's running right now. If it isn't running right now, the restart is pointless. Consider the example (from Bugzilla[1]): sleep.service takes some time to start. hello.service has After=sleep.service. Both services get started. Two jobs will appear: hello.service/start waiting sleep.service/start running Then someone runs "systemctl try-restart hello.service". Currently the try-restart operation will block and wait for sleep.service/start to complete. The correct result is to complete the try-restart operation immediately with success, because hello.service is not running. The two original jobs must not be disturbed by this. To fix this we introduce two new concepts: - a new job type: JOB_NOP A JOB_NOP job does not do anything to the unit. It does not pull in any dependencies. It is always immediately runnable. When installed to a unit, it sits in a special slot (u->nop_job) where it never conflicts with the installed job (u->job) of a different type. It never merges with jobs of other types, but it can merge into an already installed JOB_NOP job. - "collapsing" of job types When a job of one of the two special types is added, the state of the unit is checked immediately and the job type changes: JOB_TRY_RESTART -> JOB_RESTART or JOB_NOP JOB_RELOAD_OR_START -> JOB_RELOAD or JOB_START Should a job type JOB_RELOAD_OR_START appear later during job merging, it collapses immediately afterwards. Collapsing actually makes some things simpler, because there are now fewer job types that are allowed in the transaction. [1] Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=753586
2012-04-25 11:58:27 +02:00
if (u->nop_job)
job_dump(u->nop_job, f, prefix2);
2010-02-03 14:21:48 +01:00
free(p2);
2010-01-26 21:39:06 +01:00
}
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin(Unit *u) {
2010-04-06 02:43:58 +02:00
int r;
assert(u);
/* Load a .service file */
if ((r = unit_load_fragment(u)) < 0)
2010-04-06 02:43:58 +02:00
return r;
if (u->load_state == UNIT_STUB)
2010-04-06 02:43:58 +02:00
return -ENOENT;
/* Load drop-in directory data */
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
return r;
return 0;
}
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin_optional(Unit *u) {
2010-04-06 02:43:58 +02:00
int r;
2010-01-26 21:39:06 +01:00
assert(u);
2010-04-06 02:43:58 +02:00
/* Same as unit_load_fragment_and_dropin(), but whether
* something can be loaded or not doesn't matter. */
/* Load a .service file */
if ((r = unit_load_fragment(u)) < 0)
2010-01-26 21:39:06 +01:00
return r;
if (u->load_state == UNIT_STUB)
u->load_state = UNIT_LOADED;
2010-01-28 02:44:47 +01:00
2010-01-26 21:39:06 +01:00
/* Load drop-in directory data */
2010-04-06 02:43:58 +02:00
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
2010-01-26 21:39:06 +01:00
return r;
2010-04-06 02:43:58 +02:00
return 0;
2010-01-26 21:39:06 +01:00
}
int unit_add_default_target_dependency(Unit *u, Unit *target) {
assert(u);
assert(target);
if (target->type != UNIT_TARGET)
return 0;
/* Only add the dependency if both units are loaded, so that
* that loop check below is reliable */
if (u->load_state != UNIT_LOADED ||
target->load_state != UNIT_LOADED)
return 0;
/* If either side wants no automatic dependencies, then let's
* skip this */
if (!u->default_dependencies ||
!target->default_dependencies)
return 0;
/* Don't create loops */
if (set_get(target->dependencies[UNIT_BEFORE], u))
return 0;
return unit_add_dependency(target, UNIT_AFTER, u, true);
}
static int unit_add_default_dependencies(Unit *u) {
static const UnitDependency deps[] = {
UNIT_REQUIRED_BY,
UNIT_REQUIRED_BY_OVERRIDABLE,
UNIT_WANTED_BY,
UNIT_BOUND_BY
};
Unit *target;
Iterator i;
int r;
unsigned k;
assert(u);
for (k = 0; k < ELEMENTSOF(deps); k++)
SET_FOREACH(target, u->dependencies[deps[k]], i)
if ((r = unit_add_default_target_dependency(u, target)) < 0)
return r;
return 0;
}
2010-01-26 21:39:06 +01:00
int unit_load(Unit *u) {
int r;
assert(u);
if (u->in_load_queue) {
LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u);
u->in_load_queue = false;
2010-01-26 21:39:06 +01:00
}
if (u->type == _UNIT_TYPE_INVALID)
return -EINVAL;
if (u->load_state != UNIT_STUB)
2010-01-26 21:39:06 +01:00
return 0;
if (UNIT_VTABLE(u)->load)
if ((r = UNIT_VTABLE(u)->load(u)) < 0)
2010-01-26 21:39:06 +01:00
goto fail;
2010-04-06 02:43:58 +02:00
if (u->load_state == UNIT_STUB) {
2010-04-06 02:43:58 +02:00
r = -ENOENT;
goto fail;
}
if (u->load_state == UNIT_LOADED &&
u->default_dependencies)
if ((r = unit_add_default_dependencies(u)) < 0)
goto fail;
if (u->load_state == UNIT_LOADED) {
r = unit_add_mount_links(u);
if (r < 0)
return r;
}
if (u->on_failure_isolate &&
set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
log_error("More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.",
u->id);
r = -EINVAL;
goto fail;
}
assert((u->load_state != UNIT_MERGED) == !u->merged_into);
2010-04-06 02:43:58 +02:00
unit_add_to_dbus_queue(unit_follow_merge(u));
unit_add_to_gc_queue(u);
2010-01-26 21:39:06 +01:00
return 0;
fail:
u->load_state = UNIT_ERROR;
u->load_error = r;
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
2010-04-06 02:43:58 +02:00
log_debug("Failed to load configuration for %s: %s", u->id, strerror(-r));
2010-04-06 02:43:58 +02:00
2010-01-26 21:39:06 +01:00
return r;
}
bool unit_condition_test(Unit *u) {
assert(u);
dual_timestamp_get(&u->condition_timestamp);
u->condition_result = condition_test_list(u->conditions);
return u->condition_result;
}
static void unit_status_print_starting_stopping(Unit *u, bool stopping) {
const UnitStatusMessageFormats *format_table;
const char *format;
format_table = &UNIT_VTABLE(u)->status_message_formats;
if (!format_table)
return;
format = format_table->starting_stopping[stopping];
if (!format)
return;
unit_status_printf(u, "", format, unit_description(u));
}
2010-01-26 21:39:06 +01:00
/* Errors:
* -EBADR: This unit type does not support starting.
* -EALREADY: Unit is already started.
* -EAGAIN: An operation is already in progress. Retry later.
* -ECANCELED: Too many requests for now.
2010-01-26 21:39:06 +01:00
*/
int unit_start(Unit *u) {
UnitActiveState state;
2010-11-14 23:26:53 +01:00
Unit *following;
2010-01-26 21:39:06 +01:00
assert(u);
if (u->load_state != UNIT_LOADED)
return -EINVAL;
/* If this is already started, then this will succeed. Note
* that this will even succeed if this unit is not startable
* by the user. This is relied on to detect when we need to
* wait for units and when waiting is finished. */
2010-01-26 21:39:06 +01:00
state = unit_active_state(u);
if (UNIT_IS_ACTIVE_OR_RELOADING(state))
return -EALREADY;
/* If the conditions failed, don't do anything at all. If we
* already are activating this call might still be useful to
* speed up activation in case there is some hold-off time,
* but we don't want to recheck the condition in that case. */
if (state != UNIT_ACTIVATING &&
!unit_condition_test(u)) {
log_debug("Starting of %s requested but condition failed. Ignoring.", u->id);
return -EALREADY;
}
2010-11-14 23:26:53 +01:00
/* Forward to the main object, if we aren't it. */
if ((following = unit_following(u))) {
log_debug("Redirecting start request from %s to %s.", u->id, following->id);
2010-11-14 23:26:53 +01:00
return unit_start(following);
}
unit_status_print_starting_stopping(u, false);
2010-11-14 23:26:53 +01:00
/* If it is stopped, but we cannot start it, then fail */
if (!UNIT_VTABLE(u)->start)
return -EBADR;
2010-01-26 21:39:06 +01:00
/* We don't suppress calls to ->start() here when we are
* already starting, to allow this request to be used as a
* "hurry up" call, for example when the unit is in some "auto
* restart" state where it waits for a holdoff timer to elapse
* before it will start again. */
unit_add_to_dbus_queue(u);
2010-01-26 21:39:06 +01:00
return UNIT_VTABLE(u)->start(u);
}
bool unit_can_start(Unit *u) {
assert(u);
return !!UNIT_VTABLE(u)->start;
}
2010-08-30 22:45:46 +02:00
bool unit_can_isolate(Unit *u) {
assert(u);
return unit_can_start(u) &&
u->allow_isolate;
2010-08-30 22:45:46 +02:00
}
2010-01-26 21:39:06 +01:00
/* Errors:
* -EBADR: This unit type does not support stopping.
* -EALREADY: Unit is already stopped.
* -EAGAIN: An operation is already in progress. Retry later.
*/
int unit_stop(Unit *u) {
UnitActiveState state;
2010-11-14 23:26:53 +01:00
Unit *following;
2010-01-26 21:39:06 +01:00
assert(u);
state = unit_active_state(u);
if (UNIT_IS_INACTIVE_OR_FAILED(state))
2010-01-26 21:39:06 +01:00
return -EALREADY;
2010-11-14 23:26:53 +01:00
if ((following = unit_following(u))) {
log_debug("Redirecting stop request from %s to %s.", u->id, following->id);
2010-11-14 23:26:53 +01:00
return unit_stop(following);
}
unit_status_print_starting_stopping(u, true);
if (!UNIT_VTABLE(u)->stop)
return -EBADR;
unit_add_to_dbus_queue(u);
2010-01-26 21:39:06 +01:00
return UNIT_VTABLE(u)->stop(u);
}
/* Errors:
* -EBADR: This unit type does not support reloading.
* -ENOEXEC: Unit is not started.
* -EAGAIN: An operation is already in progress. Retry later.
*/
int unit_reload(Unit *u) {
UnitActiveState state;
2010-11-14 23:26:53 +01:00
Unit *following;
2010-01-26 21:39:06 +01:00
assert(u);
if (u->load_state != UNIT_LOADED)
return -EINVAL;
2010-01-26 21:39:06 +01:00
if (!unit_can_reload(u))
return -EBADR;
state = unit_active_state(u);
if (state == UNIT_RELOADING)
2010-01-26 21:39:06 +01:00
return -EALREADY;
if (state != UNIT_ACTIVE)
2010-01-26 21:39:06 +01:00
return -ENOEXEC;
2010-11-14 23:26:53 +01:00
if ((following = unit_following(u))) {
log_debug("Redirecting reload request from %s to %s.", u->id, following->id);
2010-11-14 23:26:53 +01:00
return unit_reload(following);
}
unit_add_to_dbus_queue(u);
2010-01-26 21:39:06 +01:00
return UNIT_VTABLE(u)->reload(u);
}
bool unit_can_reload(Unit *u) {
assert(u);
if (!UNIT_VTABLE(u)->reload)
return false;
if (!UNIT_VTABLE(u)->can_reload)
return true;
return UNIT_VTABLE(u)->can_reload(u);
}
static void unit_check_unneeded(Unit *u) {
Iterator i;
Unit *other;
assert(u);
/* If this service shall be shut down when unneeded then do
* so. */
if (!u->stop_when_unneeded)
return;
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
return;
SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
if (unit_pending_active(other))
return;
SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
if (unit_pending_active(other))
return;
SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i)
if (unit_pending_active(other))
return;
SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
if (unit_pending_active(other))
return;
log_info("Service %s is not needed anymore. Stopping.", u->id);
/* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
}
2010-01-26 21:39:06 +01:00
static void retroactively_start_dependencies(Unit *u) {
Iterator i;
Unit *other;
assert(u);
assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)));
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
2010-01-26 21:39:06 +01:00
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
2010-01-26 21:39:06 +01:00
SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
2010-01-26 21:39:06 +01:00
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
2010-01-26 21:39:06 +01:00
SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
2010-01-26 21:39:06 +01:00
}
static void retroactively_stop_dependencies(Unit *u) {
Iterator i;
Unit *other;
assert(u);
assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
/* Pull down units which are bound to us recursively if enabled */
SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
}
static void check_unneeded_dependencies(Unit *u) {
Iterator i;
Unit *other;
assert(u);
assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
/* Garbage collect services that might not be needed anymore, if enabled */
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
2010-01-26 21:39:06 +01:00
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
2010-01-26 21:39:06 +01:00
}
void unit_trigger_on_failure(Unit *u) {
Unit *other;
Iterator i;
assert(u);
if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0)
2011-04-07 04:11:31 +02:00
return;
log_info("Triggering OnFailure= dependencies of %s.", u->id);
2011-04-07 04:11:31 +02:00
SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) {
2011-04-07 04:11:31 +02:00
int r;
if ((r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0)
2011-04-07 04:11:31 +02:00
log_error("Failed to enqueue OnFailure= job: %s", strerror(-r));
}
}
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
bool unexpected;
2010-01-26 21:39:06 +01:00
assert(u);
assert(os < _UNIT_ACTIVE_STATE_MAX);
assert(ns < _UNIT_ACTIVE_STATE_MAX);
/* Note that this is called for all low-level state changes,
* even if they might map to the same high-level
* UnitActiveState! That means that ns == os is OK an expected
* behaviour here. For example: if a mount point is remounted
* this function will be called too! */
2010-01-26 21:39:06 +01:00
if (u->manager->n_reloading <= 0) {
dual_timestamp ts;
dual_timestamp_get(&ts);
if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns))
u->inactive_exit_timestamp = ts;
else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns))
u->inactive_enter_timestamp = ts;
if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns))
u->active_enter_timestamp = ts;
else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns))
u->active_exit_timestamp = ts;
timer_unit_notify(u, ns);
path_unit_notify(u, ns);
}
2010-01-26 21:39:06 +01:00
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
cgroup_bonding_trim_list(u->cgroup_bondings, true);
2010-07-10 17:34:42 +02:00
if (u->job) {
unexpected = false;
2010-01-26 21:39:06 +01:00
if (u->job->state == JOB_WAITING)
2010-01-26 21:39:06 +01:00
/* So we reached a different state for this
* job. Let's see if we can run it now if it
* failed previously due to EAGAIN. */
job_add_to_run_queue(u->job);
2010-01-26 21:39:06 +01:00
/* Let's check whether this state change constitutes a
* finished job, or maybe contradicts a running job and
* hence needs to invalidate jobs. */
2010-01-26 21:39:06 +01:00
switch (u->job->type) {
2010-01-26 21:39:06 +01:00
case JOB_START:
case JOB_VERIFY_ACTIVE:
2010-01-26 21:39:06 +01:00
if (UNIT_IS_ACTIVE_OR_RELOADING(ns))
job_finish_and_invalidate(u->job, JOB_DONE, true);
else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) {
unexpected = true;
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true);
}
2010-01-26 21:39:06 +01:00
break;
2010-01-26 21:39:06 +01:00
case JOB_RELOAD:
case JOB_RELOAD_OR_START:
2010-01-26 21:39:06 +01:00
if (u->job->state == JOB_RUNNING) {
if (ns == UNIT_ACTIVE)
job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true);
else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) {
unexpected = true;
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true);
}
}
2010-01-26 21:39:06 +01:00
break;
2010-01-26 21:39:06 +01:00
case JOB_STOP:
case JOB_RESTART:
case JOB_TRY_RESTART:
2010-01-26 21:39:06 +01:00
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
job_finish_and_invalidate(u->job, JOB_DONE, true);
else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) {
unexpected = true;
job_finish_and_invalidate(u->job, JOB_FAILED, true);
}
2010-01-26 21:39:06 +01:00
break;
2010-01-26 21:39:06 +01:00
default:
assert_not_reached("Job type unknown");
2010-01-26 21:39:06 +01:00
}
} else
unexpected = true;
if (u->manager->n_reloading <= 0) {
/* If this state change happened without being
* requested by a job, then let's retroactively start
* or stop dependencies. We skip that step when
* deserializing, since we don't want to create any
* additional jobs just because something is already
* activated. */
if (unexpected) {
if (UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns))
retroactively_start_dependencies(u);
else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
retroactively_stop_dependencies(u);
}
/* stop unneeded units regardless if going down was expected or not */
if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
check_unneeded_dependencies(u);
if (ns != os && ns == UNIT_FAILED) {
log_notice("Unit %s entered failed state.", u->id);
unit_trigger_on_failure(u);
}
}
/* Some names are special */
if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
if (unit_has_name(u, SPECIAL_DBUS_SERVICE))
/* The bus just might have become available,
* hence try to connect to it, if we aren't
* yet connected. */
bus_init(u->manager, true);
if (u->type == UNIT_SERVICE &&
!UNIT_IS_ACTIVE_OR_RELOADING(os) &&
u->manager->n_reloading <= 0) {
/* Write audit record if we have just finished starting up */
manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true);
u->in_audit = true;
}
if (!UNIT_IS_ACTIVE_OR_RELOADING(os))
manager_send_unit_plymouth(u->manager, u);
} else {
/* We don't care about D-Bus here, since we'll get an
* asynchronous notification for it anyway. */
if (u->type == UNIT_SERVICE &&
UNIT_IS_INACTIVE_OR_FAILED(ns) &&
!UNIT_IS_INACTIVE_OR_FAILED(os) &&
u->manager->n_reloading <= 0) {
/* Hmm, if there was no start record written
* write it now, so that we always have a nice
* pair */
if (!u->in_audit) {
manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
if (ns == UNIT_INACTIVE)
manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true);
} else
/* Write audit record if we have just finished shutting down */
manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
u->in_audit = false;
}
}
manager_recheck_journal(u->manager);
/* Maybe we finished startup and are now ready for being
* stopped because unneeded? */
unit_check_unneeded(u);
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
2010-01-26 21:39:06 +01:00
}
2010-01-27 04:31:52 +01:00
int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) {
2010-01-26 21:39:06 +01:00
struct epoll_event ev;
assert(u);
assert(fd >= 0);
2010-01-27 04:31:52 +01:00
assert(w);
assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->data.unit == u));
2010-01-26 21:39:06 +01:00
zero(ev);
2010-01-27 04:31:52 +01:00
ev.data.ptr = w;
2010-01-26 21:39:06 +01:00
ev.events = events;
if (epoll_ctl(u->manager->epoll_fd,
2010-01-27 04:31:52 +01:00
w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD,
fd,
&ev) < 0)
return -errno;
2010-01-26 21:39:06 +01:00
2010-01-27 04:31:52 +01:00
w->fd = fd;
w->type = WATCH_FD;
w->data.unit = u;
2010-01-26 21:39:06 +01:00
2010-01-27 04:31:52 +01:00
return 0;
2010-01-26 21:39:06 +01:00
}
2010-01-27 04:31:52 +01:00
void unit_unwatch_fd(Unit *u, Watch *w) {
2010-01-26 21:39:06 +01:00
assert(u);
2010-01-27 04:31:52 +01:00
assert(w);
2010-01-26 21:39:06 +01:00
2010-01-27 04:31:52 +01:00
if (w->type == WATCH_INVALID)
return;
assert(w->type == WATCH_FD);
assert(w->data.unit == u);
assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
2010-01-27 04:31:52 +01:00
w->fd = -1;
w->type = WATCH_INVALID;
w->data.unit = NULL;
2010-01-26 21:39:06 +01:00
}
int unit_watch_pid(Unit *u, pid_t pid) {
assert(u);
assert(pid >= 1);
/* Watch a specific PID. We only support one unit watching
* each PID for now. */
return hashmap_put(u->manager->watch_pids, LONG_TO_PTR(pid), u);
2010-01-26 21:39:06 +01:00
}
void unit_unwatch_pid(Unit *u, pid_t pid) {
assert(u);
assert(pid >= 1);
hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u);
2010-01-26 21:39:06 +01:00
}
2010-01-27 04:31:52 +01:00
int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
2010-01-26 21:39:06 +01:00
struct itimerspec its;
2010-01-27 04:31:52 +01:00
int flags, fd;
2010-01-26 21:39:06 +01:00
bool ours;
assert(u);
2010-01-27 04:31:52 +01:00
assert(w);
assert(w->type == WATCH_INVALID || (w->type == WATCH_UNIT_TIMER && w->data.unit == u));
2010-01-26 21:39:06 +01:00
/* This will try to reuse the old timer if there is one */
if (w->type == WATCH_UNIT_TIMER) {
assert(w->data.unit == u);
assert(w->fd >= 0);
2010-01-26 21:39:06 +01:00
ours = false;
2010-01-27 04:31:52 +01:00
fd = w->fd;
} else if (w->type == WATCH_INVALID) {
2010-01-26 21:39:06 +01:00
ours = true;
if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0)
return -errno;
} else
assert_not_reached("Invalid watch type");
2010-01-26 21:39:06 +01:00
zero(its);
if (delay <= 0) {
/* Set absolute time in the past, but not 0, since we
* don't want to disarm the timer */
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 1;
flags = TFD_TIMER_ABSTIME;
} else {
timespec_store(&its.it_value, delay);
flags = 0;
}
/* This will also flush the elapse counter */
if (timerfd_settime(fd, flags, &its, NULL) < 0)
goto fail;
2010-01-27 04:31:52 +01:00
if (w->type == WATCH_INVALID) {
struct epoll_event ev;
2010-01-26 21:39:06 +01:00
2010-01-27 04:31:52 +01:00
zero(ev);
ev.data.ptr = w;
2010-01-29 06:45:59 +01:00
ev.events = EPOLLIN;
2010-01-27 04:31:52 +01:00
if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
2010-01-27 04:31:52 +01:00
goto fail;
}
w->type = WATCH_UNIT_TIMER;
2010-01-27 04:31:52 +01:00
w->fd = fd;
w->data.unit = u;
2010-01-26 21:39:06 +01:00
return 0;
fail:
if (ours)
close_nointr_nofail(fd);
2010-01-26 21:39:06 +01:00
return -errno;
}
2010-01-27 04:31:52 +01:00
void unit_unwatch_timer(Unit *u, Watch *w) {
2010-01-26 21:39:06 +01:00
assert(u);
2010-01-27 04:31:52 +01:00
assert(w);
2010-01-26 21:39:06 +01:00
2010-01-27 04:31:52 +01:00
if (w->type == WATCH_INVALID)
2010-01-26 21:39:06 +01:00
return;
assert(w->type == WATCH_UNIT_TIMER);
assert(w->data.unit == u);
assert(w->fd >= 0);
2010-01-27 04:31:52 +01:00
assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
2010-04-21 03:27:44 +02:00
close_nointr_nofail(w->fd);
2010-01-27 04:31:52 +01:00
w->fd = -1;
w->type = WATCH_INVALID;
w->data.unit = NULL;
2010-01-26 21:39:06 +01:00
}
bool unit_job_is_applicable(Unit *u, JobType j) {
assert(u);
assert(j >= 0 && j < _JOB_TYPE_MAX);
switch (j) {
case JOB_VERIFY_ACTIVE:
case JOB_START:
case JOB_STOP:
core: add NOP jobs, job type collapsing Two of our current job types are special: JOB_TRY_RESTART, JOB_RELOAD_OR_START. They differ from other job types by being sensitive to the unit active state. They perform some action when the unit is active and some other action otherwise. This raises a question: when exactly should the unit state be checked to make the decision? Currently the unit state is checked when the job becomes runnable. It's more sensible to check the state immediately when the job is added by the user. When the user types "systemctl try-restart foo.service", he really intends to restart the service if it's running right now. If it isn't running right now, the restart is pointless. Consider the example (from Bugzilla[1]): sleep.service takes some time to start. hello.service has After=sleep.service. Both services get started. Two jobs will appear: hello.service/start waiting sleep.service/start running Then someone runs "systemctl try-restart hello.service". Currently the try-restart operation will block and wait for sleep.service/start to complete. The correct result is to complete the try-restart operation immediately with success, because hello.service is not running. The two original jobs must not be disturbed by this. To fix this we introduce two new concepts: - a new job type: JOB_NOP A JOB_NOP job does not do anything to the unit. It does not pull in any dependencies. It is always immediately runnable. When installed to a unit, it sits in a special slot (u->nop_job) where it never conflicts with the installed job (u->job) of a different type. It never merges with jobs of other types, but it can merge into an already installed JOB_NOP job. - "collapsing" of job types When a job of one of the two special types is added, the state of the unit is checked immediately and the job type changes: JOB_TRY_RESTART -> JOB_RESTART or JOB_NOP JOB_RELOAD_OR_START -> JOB_RELOAD or JOB_START Should a job type JOB_RELOAD_OR_START appear later during job merging, it collapses immediately afterwards. Collapsing actually makes some things simpler, because there are now fewer job types that are allowed in the transaction. [1] Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=753586
2012-04-25 11:58:27 +02:00
case JOB_NOP:
2010-01-26 21:39:06 +01:00
return true;
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:
return unit_can_reload(u) && unit_can_start(u);
default:
assert_not_reached("Invalid job type");
}
}
int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) {
2010-01-26 21:39:06 +01:00
static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_REQUIRES] = UNIT_REQUIRED_BY,
2010-04-15 03:11:11 +02:00
[UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
2010-01-26 21:39:06 +01:00
[UNIT_WANTS] = UNIT_WANTED_BY,
[UNIT_REQUISITE] = UNIT_REQUIRED_BY,
2010-04-15 03:11:11 +02:00
[UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
[UNIT_BINDS_TO] = UNIT_BOUND_BY,
2010-01-26 21:39:06 +01:00
[UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID,
2010-04-15 03:11:11 +02:00
[UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID,
2010-01-26 21:39:06 +01:00
[UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID,
[UNIT_BOUND_BY] = UNIT_BINDS_TO,
[UNIT_CONFLICTS] = UNIT_CONFLICTED_BY,
[UNIT_CONFLICTED_BY] = UNIT_CONFLICTS,
2010-01-26 21:39:06 +01:00
[UNIT_BEFORE] = UNIT_AFTER,
[UNIT_AFTER] = UNIT_BEFORE,
[UNIT_ON_FAILURE] = _UNIT_DEPENDENCY_INVALID,
[UNIT_REFERENCES] = UNIT_REFERENCED_BY,
[UNIT_REFERENCED_BY] = UNIT_REFERENCES,
[UNIT_TRIGGERS] = UNIT_TRIGGERED_BY,
[UNIT_TRIGGERED_BY] = UNIT_TRIGGERS,
[UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM,
[UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO
2010-01-26 21:39:06 +01:00
};
int r, q = 0, v = 0, w = 0;
2010-01-26 21:39:06 +01:00
assert(u);
assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX);
assert(other);
u = unit_follow_merge(u);
other = unit_follow_merge(other);
2010-01-26 21:39:06 +01:00
/* We won't allow dependencies on ourselves. We will not
* consider them an error however. */
if (u == other)
return 0;
if ((r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func)) < 0)
2010-01-26 21:39:06 +01:00
return r;
if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID)
if ((r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0)
return r;
if (add_reference)
if ((r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 ||
(r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0)
return r;
2010-01-26 21:39:06 +01:00
if ((q = set_put(u->dependencies[d], other)) < 0)
return q;
2010-01-26 21:39:06 +01:00
if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID)
if ((v = set_put(other->dependencies[inverse_table[d]], u)) < 0) {
r = v;
goto fail;
}
if (add_reference) {
if ((w = set_put(u->dependencies[UNIT_REFERENCES], other)) < 0) {
r = w;
goto fail;
}
if ((r = set_put(other->dependencies[UNIT_REFERENCED_BY], u)) < 0)
goto fail;
2010-01-26 21:39:06 +01:00
}
unit_add_to_dbus_queue(u);
2010-01-26 21:39:06 +01:00
return 0;
fail:
if (q > 0)
set_remove(u->dependencies[d], other);
if (v > 0)
set_remove(other->dependencies[inverse_table[d]], u);
if (w > 0)
set_remove(u->dependencies[UNIT_REFERENCES], other);
return r;
2010-01-26 21:39:06 +01:00
}
2010-01-27 00:15:56 +01:00
int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference) {
int r;
assert(u);
if ((r = unit_add_dependency(u, d, other, add_reference)) < 0)
return r;
if ((r = unit_add_dependency(u, e, other, add_reference)) < 0)
return r;
return 0;
}
2010-04-15 03:11:11 +02:00
static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) {
char *s;
assert(u);
assert(name || path);
if (!name)
2012-05-07 21:36:12 +02:00
name = path_get_file_name(path);
2010-04-15 03:11:11 +02:00
if (!unit_name_is_template(name)) {
*p = NULL;
return name;
}
if (u->instance)
s = unit_name_replace_instance(name, u->instance);
2010-04-15 03:11:11 +02:00
else {
char *i;
if (!(i = unit_name_to_prefix(u->id)))
2010-04-15 03:11:11 +02:00
return NULL;
s = unit_name_replace_instance(name, i);
free(i);
}
if (!s)
return NULL;
*p = s;
return s;
}
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
Unit *other;
int r;
2010-04-15 03:11:11 +02:00
char *s;
2010-04-15 03:11:11 +02:00
assert(u);
assert(name || path);
2010-04-15 03:11:11 +02:00
if (!(name = resolve_template(u, name, path, &s)))
return -ENOMEM;
if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
2010-04-15 03:11:11 +02:00
goto finish;
r = unit_add_dependency(u, d, other, add_reference);
2010-04-15 03:11:11 +02:00
finish:
free(s);
return r;
}
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
Unit *other;
int r;
char *s;
assert(u);
assert(name || path);
if (!(name = resolve_template(u, name, path, &s)))
return -ENOMEM;
if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
goto finish;
r = unit_add_two_dependencies(u, d, e, other, add_reference);
finish:
free(s);
return r;
}
int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
Unit *other;
int r;
2010-04-15 03:11:11 +02:00
char *s;
2010-04-15 03:11:11 +02:00
assert(u);
assert(name || path);
2010-04-15 03:11:11 +02:00
if (!(name = resolve_template(u, name, path, &s)))
return -ENOMEM;
if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
2010-04-15 03:11:11 +02:00
goto finish;
r = unit_add_dependency(other, d, u, add_reference);
2010-04-15 03:11:11 +02:00
finish:
free(s);
return r;
}
int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
Unit *other;
int r;
char *s;
assert(u);
assert(name || path);
if (!(name = resolve_template(u, name, path, &s)))
return -ENOMEM;
if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
goto finish;
if ((r = unit_add_two_dependencies(other, d, e, u, add_reference)) < 0)
goto finish;
finish:
free(s);
return r;
}
2010-01-27 00:15:56 +01:00
int set_unit_path(const char *p) {
char *cwd, *c;
int r;
/* This is mostly for debug purposes */
if (path_is_absolute(p)) {
if (!(c = strdup(p)))
return -ENOMEM;
} else {
if (!(cwd = get_current_dir_name()))
return -errno;
r = asprintf(&c, "%s/%s", cwd, p);
free(cwd);
if (r < 0)
return -ENOMEM;
}
2010-02-13 01:07:02 +01:00
if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) {
2010-01-27 00:15:56 +01:00
r = -errno;
free(c);
return r;
}
return 0;
}
2010-01-28 06:44:30 +01:00
char *unit_dbus_path(Unit *u) {
assert(u);
if (!u->id)
return NULL;
return unit_dbus_path_from_name(u->id);
}
2010-03-31 16:29:55 +02:00
int unit_add_cgroup(Unit *u, CGroupBonding *b) {
int r;
assert(u);
assert(b);
2010-03-31 16:29:55 +02:00
assert(b->path);
if (!b->controller) {
if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER)))
return -ENOMEM;
b->ours = true;
}
2010-03-31 16:29:55 +02:00
/* Ensure this hasn't been added yet */
assert(!b->unit);
if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
CGroupBonding *l;
2010-03-31 16:29:55 +02:00
l = hashmap_get(u->manager->cgroup_bondings, b->path);
LIST_PREPEND(CGroupBonding, by_path, l, b);
if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) {
LIST_REMOVE(CGroupBonding, by_path, l, b);
return r;
}
2010-03-31 16:29:55 +02:00
}
LIST_PREPEND(CGroupBonding, by_unit, u->cgroup_bondings, b);
2010-03-31 16:29:55 +02:00
b->unit = u;
return 0;
}
static char *default_cgroup_path(Unit *u) {
char *p;
assert(u);
if (u->instance) {
char *t;
t = unit_name_template(u->id);
2011-08-01 02:45:51 +02:00
if (!t)
return NULL;
p = strjoin(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL);
free(t);
} else
p = strjoin(u->manager->cgroup_hierarchy, "/", u->id, NULL);
2011-08-01 02:45:51 +02:00
return p;
}
2010-03-31 16:29:55 +02:00
int unit_add_cgroup_from_text(Unit *u, const char *name) {
char *controller = NULL, *path = NULL;
CGroupBonding *b = NULL;
bool ours = false;
2010-03-31 16:29:55 +02:00
int r;
assert(u);
assert(name);
if ((r = cg_split_spec(name, &controller, &path)) < 0)
return r;
2010-03-31 16:29:55 +02:00
if (!path) {
path = default_cgroup_path(u);
ours = true;
}
if (!controller) {
controller = strdup(SYSTEMD_CGROUP_CONTROLLER);
ours = true;
}
if (!path || !controller) {
free(path);
free(controller);
return -ENOMEM;
2010-03-31 16:29:55 +02:00
}
if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) {
r = -EEXIST;
goto fail;
}
2010-03-31 16:29:55 +02:00
if (!(b = new0(CGroupBonding, 1))) {
2010-03-31 16:29:55 +02:00
r = -ENOMEM;
goto fail;
}
b->controller = controller;
b->path = path;
b->ours = ours;
b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
2010-03-31 16:29:55 +02:00
if ((r = unit_add_cgroup(u, b)) < 0)
goto fail;
return 0;
fail:
free(path);
free(controller);
2010-03-31 16:29:55 +02:00
free(b);
return r;
}
static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
CGroupBonding *b = NULL;
2010-03-31 16:29:55 +02:00
int r = -ENOMEM;
assert(u);
if (!controller)
controller = SYSTEMD_CGROUP_CONTROLLER;
2010-03-31 16:29:55 +02:00
if (cgroup_bonding_find_list(u->cgroup_bondings, controller))
return 0;
2010-03-31 16:29:55 +02:00
if (!(b = new0(CGroupBonding, 1)))
return -ENOMEM;
2010-03-31 16:29:55 +02:00
if (!(b->controller = strdup(controller)))
goto fail;
if (!(b->path = default_cgroup_path(u)))
goto fail;
b->ours = true;
b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
if ((r = unit_add_cgroup(u, b)) < 0)
goto fail;
2010-03-31 16:29:55 +02:00
return 0;
fail:
free(b->path);
free(b->controller);
free(b);
2010-03-31 16:29:55 +02:00
return r;
}
int unit_add_default_cgroups(Unit *u) {
CGroupAttribute *a;
char **c;
int r;
assert(u);
/* Adds in the default cgroups, if they weren't specified
* otherwise. */
if (!u->manager->cgroup_hierarchy)
return 0;
if ((r = unit_add_one_default_cgroup(u, NULL)) < 0)
return r;
STRV_FOREACH(c, u->manager->default_controllers)
unit_add_one_default_cgroup(u, *c);
LIST_FOREACH(by_unit, a, u->cgroup_attributes)
unit_add_one_default_cgroup(u, a->controller);
return 0;
}
2010-03-31 16:29:55 +02:00
CGroupBonding* unit_get_default_cgroup(Unit *u) {
assert(u);
return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER);
2010-03-31 16:29:55 +02:00
}
int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) {
int r;
char *c = NULL;
CGroupAttribute *a;
assert(u);
assert(name);
assert(value);
if (!controller) {
const char *dot;
dot = strchr(name, '.');
if (!dot)
return -EINVAL;
c = strndup(name, dot - name);
if (!c)
return -ENOMEM;
controller = c;
}
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
r = -EINVAL;
goto finish;
}
a = new0(CGroupAttribute, 1);
if (!a) {
r = -ENOMEM;
goto finish;
}
if (c) {
a->controller = c;
c = NULL;
} else
a->controller = strdup(controller);
a->name = strdup(name);
a->value = strdup(value);
if (!a->controller || !a->name || !a->value) {
free(a->controller);
free(a->name);
free(a->value);
free(a);
return -ENOMEM;
}
a->map_callback = map_callback;
LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a);
r = 0;
finish:
free(c);
return r;
}
int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
char *t;
int r;
assert(u);
assert(type);
assert(_found);
if (!(t = unit_name_change_suffix(u->id, type)))
return -ENOMEM;
assert(!unit_has_name(u, t));
r = manager_load_unit(u->manager, t, NULL, NULL, _found);
free(t);
2010-04-15 03:11:11 +02:00
assert(r < 0 || *_found != u);
return r;
}
2010-04-21 03:27:44 +02:00
int unit_get_related_unit(Unit *u, const char *type, Unit **_found) {
Unit *found;
char *t;
assert(u);
assert(type);
assert(_found);
if (!(t = unit_name_change_suffix(u->id, type)))
2010-04-21 03:27:44 +02:00
return -ENOMEM;
assert(!unit_has_name(u, t));
found = manager_get_unit(u->manager, t);
2010-04-21 03:27:44 +02:00
free(t);
if (!found)
return -ENOENT;
*_found = found;
return 0;
}
2010-04-15 03:11:11 +02:00
static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
Unit *u = userdata;
assert(u);
return unit_name_to_prefix_and_instance(u->id);
2010-04-15 03:11:11 +02:00
}
static char *specifier_prefix(char specifier, void *data, void *userdata) {
Unit *u = userdata;
assert(u);
return unit_name_to_prefix(u->id);
2010-04-15 03:11:11 +02:00
}
static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) {
Unit *u = userdata;
char *p, *r;
assert(u);
if (!(p = unit_name_to_prefix(u->id)))
2010-04-15 03:11:11 +02:00
return NULL;
r = unit_name_unescape(p);
free(p);
return r;
}
static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) {
Unit *u = userdata;
assert(u);
if (u->instance)
return unit_name_unescape(u->instance);
2010-04-15 03:11:11 +02:00
return strdup("");
}
static char *specifier_filename(char specifier, void *data, void *userdata) {
Unit *u = userdata;
assert(u);
if (u->instance)
return unit_name_path_unescape(u->instance);
return unit_name_to_path(u->instance);
}
static char *specifier_cgroup(char specifier, void *data, void *userdata) {
Unit *u = userdata;
assert(u);
return default_cgroup_path(u);
}
static char *specifier_cgroup_root(char specifier, void *data, void *userdata) {
Unit *u = userdata;
char *p;
assert(u);
if (specifier == 'r')
return strdup(u->manager->cgroup_hierarchy);
2012-05-07 21:36:12 +02:00
if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0)
return strdup("");
if (streq(p, "/")) {
free(p);
return strdup("");
}
return p;
}
static char *specifier_runtime(char specifier, void *data, void *userdata) {
Unit *u = userdata;
assert(u);
if (u->manager->running_as == MANAGER_USER) {
const char *e;
e = getenv("XDG_RUNTIME_DIR");
if (e)
return strdup(e);
}
return strdup("/run");
}
static char *specifier_user_name(char specifier, void *data, void *userdata) {
Service *s = userdata;
int r;
const char *username;
/* get USER env from our own env if set */
if (!s->exec_context.user)
return getusername_malloc();
/* fish username from passwd */
username = s->exec_context.user;
r = get_user_creds(&username, NULL, NULL, NULL, NULL);
if (r < 0)
return NULL;
return strdup(username);
}
static char *specifier_user_home(char specifier, void *data, void *userdata) {
Service *s = userdata;
int r;
const char *username, *home;
/* return HOME if set, otherwise from passwd */
if (!s->exec_context.user) {
char *h;
r = get_home_dir(&h);
if (r < 0)
return NULL;
return h;
}
username = s->exec_context.user;
r = get_user_creds(&username, NULL, NULL, &home, NULL);
if (r < 0)
return NULL;
return strdup(home);
}
static char *specifier_user_shell(char specifier, void *data, void *userdata) {
Service *s = userdata;
int r;
const char *username, *shell;
/* return HOME if set, otherwise from passwd */
if (!s->exec_context.user) {
char *sh;
r = get_shell(&sh);
if (r < 0)
return strdup("/bin/sh");
return sh;
}
username = s->exec_context.user;
r = get_user_creds(&username, NULL, NULL, NULL, &shell);
if (r < 0)
return strdup("/bin/sh");
return strdup(shell);
}
2010-04-15 03:11:11 +02:00
char *unit_name_printf(Unit *u, const char* format) {
/*
* This will use the passed string as format string and
* replace the following specifiers:
*
* %n: the full id of the unit (foo@bar.waldo)
* %N: the id of the unit without the suffix (foo@bar)
* %p: the prefix (foo)
* %i: the instance (bar)
*/
const Specifier table[] = {
{ 'n', specifier_string, u->id },
2010-04-15 03:11:11 +02:00
{ 'N', specifier_prefix_and_instance, NULL },
{ 'p', specifier_prefix, NULL },
{ 'i', specifier_string, u->instance },
2010-04-15 03:11:11 +02:00
{ 0, NULL, NULL }
};
assert(u);
assert(format);
return specifier_printf(format, table, u);
}
char *unit_full_printf(Unit *u, const char *format) {
/* This is similar to unit_name_printf() but also supports
* unescaping. Also, adds a couple of additional codes:
*
* %c cgroup path of unit
* %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711")
* %R parent of root cgroup path (e.g. "/usr/lennart/shared")
* %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
* %u the username of the configured User or running user
* %h the homedir of the configured User or running user
*/
2010-04-15 03:11:11 +02:00
const Specifier table[] = {
{ 'n', specifier_string, u->id },
2010-04-15 03:11:11 +02:00
{ 'N', specifier_prefix_and_instance, NULL },
{ 'p', specifier_prefix, NULL },
{ 'P', specifier_prefix_unescaped, NULL },
{ 'i', specifier_string, u->instance },
2010-04-15 03:11:11 +02:00
{ 'I', specifier_instance_unescaped, NULL },
{ 'f', specifier_filename, NULL },
{ 'c', specifier_cgroup, NULL },
{ 'r', specifier_cgroup_root, NULL },
{ 'R', specifier_cgroup_root, NULL },
{ 't', specifier_runtime, NULL },
{ 'u', specifier_user_name, NULL },
{ 'h', specifier_user_home, NULL },
{ 's', specifier_user_shell, NULL },
2010-04-15 03:11:11 +02:00
{ 0, NULL, NULL }
};
assert(u);
assert(format);
return specifier_printf(format, table, u);
}
char **unit_full_printf_strv(Unit *u, char **l) {
size_t n;
char **r, **i, **j;
/* Applies unit_full_printf to every entry in l */
assert(u);
n = strv_length(l);
if (!(r = new(char*, n+1)))
return NULL;
for (i = l, j = r; *i; i++, j++)
if (!(*j = unit_full_printf(u, *i)))
goto fail;
*j = NULL;
return r;
fail:
2011-03-31 15:35:40 +02:00
for (j--; j >= r; j--)
2010-04-15 03:11:11 +02:00
free(*j);
free(r);
return NULL;
}
int unit_watch_bus_name(Unit *u, const char *name) {
assert(u);
assert(name);
/* Watch a specific name on the bus. We only support one unit
* watching each name for now. */
return hashmap_put(u->manager->watch_bus, name, u);
}
void unit_unwatch_bus_name(Unit *u, const char *name) {
assert(u);
assert(name);
hashmap_remove_value(u->manager->watch_bus, name, u);
}
2010-04-21 03:27:44 +02:00
bool unit_can_serialize(Unit *u) {
assert(u);
return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
}
int unit_serialize(Unit *u, FILE *f, FDSet *fds) {
int r;
assert(u);
assert(f);
assert(fds);
if (!unit_can_serialize(u))
return 0;
if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0)
return r;
if (u->job) {
fprintf(f, "job\n");
job_serialize(u->job, f, fds);
}
core: add NOP jobs, job type collapsing Two of our current job types are special: JOB_TRY_RESTART, JOB_RELOAD_OR_START. They differ from other job types by being sensitive to the unit active state. They perform some action when the unit is active and some other action otherwise. This raises a question: when exactly should the unit state be checked to make the decision? Currently the unit state is checked when the job becomes runnable. It's more sensible to check the state immediately when the job is added by the user. When the user types "systemctl try-restart foo.service", he really intends to restart the service if it's running right now. If it isn't running right now, the restart is pointless. Consider the example (from Bugzilla[1]): sleep.service takes some time to start. hello.service has After=sleep.service. Both services get started. Two jobs will appear: hello.service/start waiting sleep.service/start running Then someone runs "systemctl try-restart hello.service". Currently the try-restart operation will block and wait for sleep.service/start to complete. The correct result is to complete the try-restart operation immediately with success, because hello.service is not running. The two original jobs must not be disturbed by this. To fix this we introduce two new concepts: - a new job type: JOB_NOP A JOB_NOP job does not do anything to the unit. It does not pull in any dependencies. It is always immediately runnable. When installed to a unit, it sits in a special slot (u->nop_job) where it never conflicts with the installed job (u->job) of a different type. It never merges with jobs of other types, but it can merge into an already installed JOB_NOP job. - "collapsing" of job types When a job of one of the two special types is added, the state of the unit is checked immediately and the job type changes: JOB_TRY_RESTART -> JOB_RESTART or JOB_NOP JOB_RELOAD_OR_START -> JOB_RELOAD or JOB_START Should a job type JOB_RELOAD_OR_START appear later during job merging, it collapses immediately afterwards. Collapsing actually makes some things simpler, because there are now fewer job types that are allowed in the transaction. [1] Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=753586
2012-04-25 11:58:27 +02:00
if (u->nop_job) {
fprintf(f, "job\n");
job_serialize(u->nop_job, f, fds);
}
dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp);
dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp);
dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp);
2011-03-17 04:36:19 +01:00
if (dual_timestamp_is_set(&u->condition_timestamp))
unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result));
2010-10-27 00:01:12 +02:00
2010-04-21 03:27:44 +02:00
/* End marker */
fputc('\n', f);
return 0;
}
void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
va_list ap;
assert(u);
assert(f);
assert(key);
assert(format);
fputs(key, f);
fputc('=', f);
va_start(ap, format);
vfprintf(f, format, ap);
va_end(ap);
fputc('\n', f);
}
void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
assert(u);
assert(f);
assert(key);
assert(value);
fprintf(f, "%s=%s\n", key, value);
}
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
int r;
assert(u);
assert(f);
assert(fds);
if (!unit_can_serialize(u))
return 0;
for (;;) {
char line[LINE_MAX], *l, *v;
2010-04-21 03:27:44 +02:00
size_t k;
if (!fgets(line, sizeof(line), f)) {
if (feof(f))
return 0;
return -errno;
}
char_array_0(line);
2010-04-21 03:27:44 +02:00
l = strstrip(line);
/* End marker */
if (l[0] == 0)
return 0;
k = strcspn(l, "=");
if (l[k] == '=') {
l[k] = 0;
v = l+k+1;
} else
v = l+k;
if (streq(l, "job")) {
if (v[0] == '\0') {
/* new-style serialized job */
Job *j = job_new_raw(u);
if (!j)
return -ENOMEM;
r = job_deserialize(j, f, fds);
if (r < 0) {
job_free(j);
return r;
}
r = hashmap_put(u->manager->jobs, UINT32_TO_PTR(j->id), j);
if (r < 0) {
job_free(j);
return r;
}
core: add NOP jobs, job type collapsing Two of our current job types are special: JOB_TRY_RESTART, JOB_RELOAD_OR_START. They differ from other job types by being sensitive to the unit active state. They perform some action when the unit is active and some other action otherwise. This raises a question: when exactly should the unit state be checked to make the decision? Currently the unit state is checked when the job becomes runnable. It's more sensible to check the state immediately when the job is added by the user. When the user types "systemctl try-restart foo.service", he really intends to restart the service if it's running right now. If it isn't running right now, the restart is pointless. Consider the example (from Bugzilla[1]): sleep.service takes some time to start. hello.service has After=sleep.service. Both services get started. Two jobs will appear: hello.service/start waiting sleep.service/start running Then someone runs "systemctl try-restart hello.service". Currently the try-restart operation will block and wait for sleep.service/start to complete. The correct result is to complete the try-restart operation immediately with success, because hello.service is not running. The two original jobs must not be disturbed by this. To fix this we introduce two new concepts: - a new job type: JOB_NOP A JOB_NOP job does not do anything to the unit. It does not pull in any dependencies. It is always immediately runnable. When installed to a unit, it sits in a special slot (u->nop_job) where it never conflicts with the installed job (u->job) of a different type. It never merges with jobs of other types, but it can merge into an already installed JOB_NOP job. - "collapsing" of job types When a job of one of the two special types is added, the state of the unit is checked immediately and the job type changes: JOB_TRY_RESTART -> JOB_RESTART or JOB_NOP JOB_RELOAD_OR_START -> JOB_RELOAD or JOB_START Should a job type JOB_RELOAD_OR_START appear later during job merging, it collapses immediately afterwards. Collapsing actually makes some things simpler, because there are now fewer job types that are allowed in the transaction. [1] Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=753586
2012-04-25 11:58:27 +02:00
r = job_install_deserialized(j);
if (r < 0) {
hashmap_remove(u->manager->jobs, UINT32_TO_PTR(j->id));
job_free(j);
return r;
}
} else {
/* legacy */
JobType type = job_type_from_string(v);
if (type < 0)
log_debug("Failed to parse job type value %s", v);
else
u->deserialized_job = type;
}
continue;
} else if (streq(l, "inactive-exit-timestamp")) {
dual_timestamp_deserialize(v, &u->inactive_exit_timestamp);
continue;
} else if (streq(l, "active-enter-timestamp")) {
dual_timestamp_deserialize(v, &u->active_enter_timestamp);
continue;
} else if (streq(l, "active-exit-timestamp")) {
dual_timestamp_deserialize(v, &u->active_exit_timestamp);
continue;
} else if (streq(l, "inactive-enter-timestamp")) {
dual_timestamp_deserialize(v, &u->inactive_enter_timestamp);
continue;
2011-03-17 04:36:19 +01:00
} else if (streq(l, "condition-timestamp")) {
dual_timestamp_deserialize(v, &u->condition_timestamp);
2011-03-17 04:36:19 +01:00
continue;
} else if (streq(l, "condition-result")) {
int b;
if ((b = parse_boolean(v)) < 0)
log_debug("Failed to parse condition result value %s", v);
else
u->condition_result = b;
2011-03-29 23:32:31 +02:00
continue;
}
2010-04-21 03:27:44 +02:00
if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0)
return r;
}
}
int unit_add_node_link(Unit *u, const char *what, bool wants) {
Unit *device;
char *e;
int r;
assert(u);
if (!what)
return 0;
/* Adds in links to the device node that this unit is based on */
if (!is_device_path(what))
return 0;
e = unit_name_from_path(what, ".device");
if (!e)
return -ENOMEM;
r = manager_load_unit(u->manager, e, NULL, NULL, &device);
free(e);
if (r < 0)
return r;
if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true)) < 0)
return r;
if (wants)
if ((r = unit_add_dependency(device, UNIT_WANTS, u, false)) < 0)
return r;
return 0;
}
2010-04-21 03:27:44 +02:00
int unit_coldplug(Unit *u) {
int r;
assert(u);
if (UNIT_VTABLE(u)->coldplug)
if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
return r;
if (u->job) {
r = job_coldplug(u->job);
if (r < 0)
return r;
} else if (u->deserialized_job >= 0) {
/* legacy */
r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL);
if (r < 0)
return r;
u->deserialized_job = _JOB_TYPE_INVALID;
}
return 0;
}
void unit_status_printf(Unit *u, const char *status, const char *format, ...) {
va_list ap;
assert(u);
assert(format);
if (!manager_get_show_status(u->manager))
return;
if (!manager_is_booting_or_shutting_down(u->manager))
return;
va_start(ap, format);
2012-01-05 15:35:16 +01:00
status_vprintf(status, true, format, ap);
va_end(ap);
}
bool unit_need_daemon_reload(Unit *u) {
struct stat st;
assert(u);
if (u->fragment_path) {
zero(st);
if (stat(u->fragment_path, &st) < 0)
/* What, cannot access this anymore? */
return true;
if (u->fragment_mtime > 0 &&
timespec_load(&st.st_mtim) != u->fragment_mtime)
return true;
}
if (u->source_path) {
zero(st);
if (stat(u->source_path, &st) < 0)
return true;
if (u->source_mtime > 0 &&
timespec_load(&st.st_mtim) != u->source_mtime)
return true;
}
return false;
}
void unit_reset_failed(Unit *u) {
assert(u);
if (UNIT_VTABLE(u)->reset_failed)
UNIT_VTABLE(u)->reset_failed(u);
}
Unit *unit_following(Unit *u) {
assert(u);
if (UNIT_VTABLE(u)->following)
return UNIT_VTABLE(u)->following(u);
return NULL;
}
2010-09-01 03:35:04 +02:00
bool unit_pending_inactive(Unit *u) {
assert(u);
/* Returns true if the unit is inactive or going down */
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)))
return true;
if (u->job && u->job->type == JOB_STOP)
2010-09-01 03:35:04 +02:00
return true;
return false;
}
bool unit_pending_active(Unit *u) {
assert(u);
/* Returns true if the unit is active or going up */
if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
return true;
if (u->job &&
(u->job->type == JOB_START ||
u->job->type == JOB_RELOAD_OR_START ||
u->job->type == JOB_RESTART))
return true;
return false;
}
2010-10-22 16:11:50 +02:00
int unit_kill(Unit *u, KillWho w, KillMode m, int signo, DBusError *error) {
assert(u);
assert(w >= 0 && w < _KILL_WHO_MAX);
assert(m >= 0 && m < _KILL_MODE_MAX);
assert(signo > 0);
assert(signo < _NSIG);
if (m == KILL_NONE)
return 0;
if (!UNIT_VTABLE(u)->kill)
return -ENOTSUP;
return UNIT_VTABLE(u)->kill(u, w, m, signo, error);
}
int unit_following_set(Unit *u, Set **s) {
assert(u);
assert(s);
if (UNIT_VTABLE(u)->following_set)
return UNIT_VTABLE(u)->following_set(u, s);
*s = NULL;
return 0;
}
2011-07-31 18:28:02 +02:00
UnitFileState unit_get_unit_file_state(Unit *u) {
assert(u);
if (u->unit_file_state < 0 && u->fragment_path)
u->unit_file_state = unit_file_get_state(
u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
2012-05-07 21:36:12 +02:00
NULL, path_get_file_name(u->fragment_path));
2011-07-31 18:28:02 +02:00
return u->unit_file_state;
2011-07-31 18:28:02 +02:00
}
Unit* unit_ref_set(UnitRef *ref, Unit *u) {
assert(ref);
assert(u);
if (ref->unit)
unit_ref_unset(ref);
ref->unit = u;
LIST_PREPEND(UnitRef, refs, u->refs, ref);
return u;
}
void unit_ref_unset(UnitRef *ref) {
assert(ref);
if (!ref->unit)
return;
LIST_REMOVE(UnitRef, refs, ref->unit->refs, ref);
ref->unit = NULL;
}
int unit_add_one_mount_link(Unit *u, Mount *m) {
char **i;
assert(u);
assert(m);
if (u->load_state != UNIT_LOADED ||
UNIT(m)->load_state != UNIT_LOADED)
return 0;
STRV_FOREACH(i, u->requires_mounts_for) {
if (UNIT(m) == u)
continue;
if (!path_startswith(*i, m->where))
continue;
return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
}
return 0;
}
int unit_add_mount_links(Unit *u) {
Unit *other;
int r;
assert(u);
LIST_FOREACH(units_by_type, other, u->manager->units_by_type[UNIT_MOUNT]) {
r = unit_add_one_mount_link(u, MOUNT(other));
if (r < 0)
return r;
}
return 0;
}
static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
[UNIT_ACTIVE] = "active",
[UNIT_RELOADING] = "reloading",
[UNIT_INACTIVE] = "inactive",
[UNIT_FAILED] = "failed",
[UNIT_ACTIVATING] = "activating",
[UNIT_DEACTIVATING] = "deactivating"
};
DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_REQUIRES] = "Requires",
2010-04-15 03:11:11 +02:00
[UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
[UNIT_WANTS] = "Wants",
[UNIT_REQUISITE] = "Requisite",
2010-04-15 03:11:11 +02:00
[UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
[UNIT_REQUIRED_BY] = "RequiredBy",
2010-04-15 03:11:11 +02:00
[UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
[UNIT_BINDS_TO] = "BindsTo",
[UNIT_WANTED_BY] = "WantedBy",
[UNIT_CONFLICTS] = "Conflicts",
[UNIT_CONFLICTED_BY] = "ConflictedBy",
[UNIT_BOUND_BY] = "BoundBy",
[UNIT_BEFORE] = "Before",
[UNIT_AFTER] = "After",
[UNIT_REFERENCES] = "References",
[UNIT_REFERENCED_BY] = "ReferencedBy",
[UNIT_ON_FAILURE] = "OnFailure",
[UNIT_TRIGGERS] = "Triggers",
[UNIT_TRIGGERED_BY] = "TriggeredBy",
[UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
[UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom"
};
DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);