manager.c revision da19d5c19f60ec80e1733b1e994311c59c6eda73
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster This file is part of systemd.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster Copyright 2010 Lennart Poettering
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster systemd is free software; you can redistribute it and/or modify it
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster under the terms of the GNU General Public License as published by
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster the Free Software Foundation; either version 2 of the License, or
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster (at your option) any later version.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster systemd is distributed in the hope that it will be useful, but
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster WITHOUT ANY WARRANTY; without even the implied warranty of
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster General Public License for more details.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster You should have received a copy of the GNU General Public License
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster along with systemd; If not, see <http://www.gnu.org/licenses/>.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster/* Where clients shall send notification messages to */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#define NOTIFY_SOCKET_SYSTEM "/run/systemd/notify"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#define NOTIFY_SOCKET_USER "@/org/freedesktop/systemd1/notify"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if ((m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster log_error("Failed to allocate notification socket: %m");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET_USER "/%llu", random_ull());
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster strncpy(sa.un.sun_path, NOTIFY_SOCKET_SYSTEM, sizeof(sa.un.sun_path));
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (!(m->notify_socket = strdup(sa.un.sun_path)))
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster log_debug("Using notification socket %s", m->notify_socket);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* Enable that we get SIGINT on control-alt-del */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster log_warning("Failed to enable ctrl-alt-del handling: %m");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY)) < 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* Enable that we get SIGWINCH on kbrequest */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster log_warning("Failed to enable kbrequest handling: %s", strerror(errno));
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* We are not interested in SIGSTOP and friends. */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGUSR1, /* systemd/upstart: reconnect to D-Bus */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGINT, /* Kernel sends us this on control-alt-del */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGWINCH, /* Kernel sends us this on kbrequest (alt-arrowup) */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGPWR, /* Some kernel drivers and upsd send us this on power failure */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGRTMIN+0, /* systemd: start default.target */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGRTMIN+1, /* systemd: isolate rescue.target */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGRTMIN+2, /* systemd: isolate emergency.target */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGRTMIN+4, /* systemd: start poweroff.target */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGRTMIN+20, /* systemd: enable status messages */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SIGRTMIN+21, /* systemd: disable status messages */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterint manager_new(ManagerRunningAs running_as, Manager **_m) {
#ifdef HAVE_AUDIT
m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1;
goto fail;
goto fail;
goto fail;
goto fail;
goto fail;
goto fail;
goto fail;
goto fail;
goto fail;
goto fail;
if ((r = manager_setup_signals(m)) < 0)
goto fail;
if ((r = manager_setup_cgroup(m)) < 0)
goto fail;
if ((r = manager_setup_notify(m)) < 0)
goto fail;
goto fail;
#ifdef HAVE_AUDIT
*_m = m;
fail:
manager_free(m);
assert(m);
Iterator i;
bool is_bad;
assert(u);
goto bad;
if (unit_check_gc(u))
goto good;
is_bad = true;
goto good;
is_bad = false;
if (is_bad)
goto bad;
bad:
good:
unsigned gc_marker;
assert(m);
(m->gc_queue_timestamp <= 0 ||
m->n_in_gc_queue = 0;
m->gc_queue_timestamp = 0;
Job *j;
Unit *u;
assert(m);
job_free(j);
unit_free(u);
UnitType c;
assert(m);
for (c = 0; c < _UNIT_TYPE_MAX; c++)
bus_done(m);
if (m->epoll_fd >= 0)
#ifdef HAVE_AUDIT
if (m->audit_fd >= 0)
free(m);
UnitType c;
assert(m);
for (c = 0; c < _UNIT_TYPE_MAX; c++)
Iterator i;
Unit *u;
assert(m);
if ((q = unit_coldplug(u)) < 0)
assert(m);
if (!(d = opendir(*i))) {
r = -ENOMEM;
goto fail;
free(p);
goto fail;
closedir(d);
d = NULL;
fail:
closedir(d);
assert(m);
if (serialization)
m->n_deserializing ++;
r = manager_enumerate(m);
if (serialization)
if ((q = manager_coldplug(m)) < 0)
if (serialization) {
m->n_deserializing --;
assert(m);
assert(j);
if (!j->installed)
job_free(j);
Job *j;
transaction_delete_job(m, j, true);
Iterator i;
Job *j;
assert(m);
while (j->subject_list)
while (j->object_list)
Job *j;
assert(m);
if (j->installed)
transaction_delete_job(m, j, true);
job_free(j);
JobDependency *l;
assert(m);
l = j->subject_list;
l = m->transaction_anchor;
if (!l->matters)
assert(j);
j->type = t;
l->subject = j;
last = l;
if (last) {
if (j->subject_list)
l->object = j;
last = l;
if (last) {
if (j->object_list)
JobDependency *l;
assert(j);
if (l->conflicts)
Job *k;
assert(j);
Job *d;
log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->meta.id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->meta.id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
if (job_is_conflicted_by(j))
if (job_is_conflicted_by(k))
} else if (!j->matters_to_anchor)
else if (!k->matters_to_anchor)
return -ENOEXEC;
log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->meta.id, job_type_to_string(d->type));
transaction_delete_job(m, d, true);
return -EINVAL;
Job *j;
Iterator i;
assert(m);
JobType t;
Job *k;
t = j->type;
if ((r = delete_one_unmergeable_job(m, j)) >= 0)
return -EAGAIN;
dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
Job *k;
while ((k = j->transaction_next)) {
if (j->installed) {
transaction_merge_and_delete_job(m, k, j, t);
transaction_merge_and_delete_job(m, j, k, t);
bool again;
assert(m);
Job *j;
Iterator i;
again = false;
bool changes_something = false;
Job *k;
if (!job_is_anchor(k) &&
changes_something = true;
if (changes_something)
/* log_debug("Found redundant job %s/%s, dropping.", j->unit->meta.id, job_type_to_string(j->type)); */
transaction_delete_job(m, j, false);
again = true;
} while (again);
assert(u);
if (j->matters_to_anchor)
static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation, DBusError *e) {
Iterator i;
Unit *u;
assert(m);
assert(j);
if (!j->marker)
if (!delete &&
!k->installed &&
delete = k;
if (delete) {
log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->meta.id, job_type_to_string(delete->type));
return -EAGAIN;
dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details.");
return -ENOEXEC;
Job *o;
Job *j;
Iterator i;
assert(m);
g = (*generation)++;
bool again;
assert(m);
Iterator i;
Job *j;
again = false;
if (j->object_list) {
transaction_delete_job(m, j, true);
again = true;
} while (again);
Iterator i;
Job *j;
assert(m);
return -EEXIST;
bool again;
assert(m);
Job *j;
Iterator i;
again = false;
if (j->matters_to_anchor)
if (changes_existing_job)
transaction_delete_job(m, j, true);
again = true;
if (again)
} while (again);
Iterator i;
Job *j;
if (j->installed)
goto rollback;
if (j->installed) {
/* log_debug("Skipping already installed job %s/%s as %u", j->unit->meta.id, job_type_to_string(j->type), (unsigned) j->id); */
j->installed = true;
m->n_installed_jobs ++;
job_start_timer(j);
log_debug("Installed new job %s/%s as %u", j->unit->meta.id, job_type_to_string(j->type), (unsigned) j->id);
if (j->installed)
assert(m);
if (r != -EAGAIN) {
log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
goto rollback;
if ((r = transaction_merge_jobs(m, e)) >= 0)
if (r != -EAGAIN) {
goto rollback;
if ((r = transaction_is_destructive(m, e)) < 0) {
goto rollback;
if ((r = transaction_apply(m)) < 0) {
goto rollback;
static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool override, bool *is_new) {
Job *j, *f;
assert(m);
if (is_new)
*is_new = false;
return NULL;
j->generation = 0;
j->matters_to_anchor = false;
job_free(j);
return NULL;
if (is_new)
*is_new = true;
assert(m);
assert(j);
if (j->transaction_prev)
else if (j->transaction_next)
if (j->transaction_next)
while (j->subject_list)
while (j->object_list) {
static int transaction_add_job_and_dependencies(
Manager *m,
bool matters,
bool override,
bool conflicts,
bool ignore_deps,
DBusError *e,
Iterator i;
bool is_new;
assert(m);
return -EINVAL;
return -EINVAL;
return -EINVAL;
dbus_set_error(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, "Job type %s is not applicable for unit %s.", job_type_to_string(type), unit->meta.id);
return -EBADR;
return -ENOMEM;
return -ENOMEM;
if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, false, override, false, false, e, NULL)) < 0) {
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, e, NULL)) < 0) {
if (r != -EBADR)
goto fail;
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, e, NULL)) < 0) {
if (r != -EBADR)
goto fail;
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, false, false, e, NULL)) < 0) {
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, false, false, e, NULL)) < 0) {
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, e, NULL)) < 0) {
if (r != -EBADR)
goto fail;
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, e, NULL)) < 0) {
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, true, false, e, NULL)) < 0) {
if (r != -EBADR)
goto fail;
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, false, override, false, false, e, NULL)) < 0) {
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, e, NULL)) < 0) {
if (r != -EBADR)
goto fail;
dbus_error_free(e);
if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, e, NULL)) < 0) {
if (r != -EBADR)
goto fail;
dbus_error_free(e);
if (_ret)
fail:
Iterator i;
Unit *u;
assert(m);
if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, u, NULL, true, false, false, false, NULL, NULL)) < 0)
int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, DBusError *e, Job **_ret) {
assert(m);
return -EINVAL;
return -EPERM;
log_debug("Trying to enqueue job %s/%s/%s", unit->meta.id, job_type_to_string(type), job_mode_to_string(mode));
if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, false, mode == JOB_IGNORE_DEPENDENCIES, e, &ret)) < 0) {
if ((r = transaction_add_isolate_jobs(m)) < 0) {
if (_ret)
int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool override, DBusError *e, Job **_ret) {
assert(m);
assert(m);
assert(m);
assert(m);
if (m->dispatching_load_queue)
m->dispatching_load_queue = true;
m->dispatching_load_queue = false;
int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret) {
assert(m);
return -EINVAL;
if (!name)
return -EINVAL;
return -ENOMEM;
if (path)
return -ENOMEM;
if (_ret)
assert(m);
if (_ret)
Iterator i;
Job *j;
assert(s);
assert(f);
Iterator i;
Unit *u;
assert(s);
assert(f);
Job *j;
assert(m);
Job *j;
if (m->dispatching_run_queue)
m->dispatching_run_queue = true;
while ((j = m->run_queue)) {
m->dispatching_run_queue = false;
Job *j;
assert(m);
if (m->dispatching_dbus_queue)
m->dispatching_dbus_queue = true;
while ((j = m->dbus_job_queue)) {
m->dispatching_dbus_queue = false;
ssize_t n;
assert(m);
} control;
Unit *u;
char **tags;
return -EIO;
return -errno;
buf[n] = 0;
return -ENOMEM;
assert(m);
Unit *u;
return -errno;
if ((r = manager_process_notify_fd(m)) < 0)
return -errno;
ssize_t n;
bool sigchld = false;
assert(m);
char *p = NULL;
return -EIO;
return -errno;
free(p);
case SIGCHLD:
sigchld = true;
case SIGTERM:
case SIGINT:
case SIGWINCH:
case SIGPWR:
case SIGUSR1: {
Unit *u;
bus_init(m, true);
case SIGUSR2: {
FILE *f;
if (ferror(f)) {
fclose(f);
fclose(f);
case SIGHUP:
static const char * const target_table[] = {
[0] = SPECIAL_DEFAULT_TARGET,
[0] = MANAGER_HALT,
m->show_status = true;
m->show_status = false;
if (sigchld)
return manager_dispatch_sigchld(m);
Watch *w;
assert(m);
switch (w->type) {
case WATCH_SIGNAL:
return -EINVAL;
if ((r = manager_process_signal_fd(m)) < 0)
case WATCH_NOTIFY:
return -EINVAL;
if ((r = manager_process_notify_fd(m)) < 0)
case WATCH_FD:
case WATCH_UNIT_TIMER:
case WATCH_JOB_TIMER: {
uint64_t v;
ssize_t k;
case WATCH_MOUNT:
case WATCH_SWAP:
case WATCH_UDEV:
case WATCH_DBUS_WATCH:
case WATCH_DBUS_TIMEOUT:
assert(m);
if ((r = manager_dispatch_sigchld(m)) < 0)
if (manager_dispatch_load_queue(m) > 0)
if (manager_dispatch_run_queue(m) > 0)
if (bus_dispatch(m) > 0)
if (manager_dispatch_cleanup_queue(m) > 0)
if (manager_dispatch_gc_queue(m) > 0)
if (manager_dispatch_dbus_queue(m) > 0)
if (swap_dispatch_reload(m) > 0)
return -errno;
return m->exit_code;
Unit *u;
assert(m);
assert(s);
return -EINVAL;
return -ENOMEM;
u = manager_get_unit(m, n);
free(n);
return -ENOENT;
*_u = u;
Job *j;
unsigned id;
assert(m);
assert(s);
return -EINVAL;
return -ENOENT;
*_j = j;
#ifdef HAVE_AUDIT
if (m->audit_fd < 0)
if (m->n_deserializing > 0)
free(p);
if (m->n_deserializing > 0)
if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
goto finish;
goto finish;
errno = 0;
goto finish;
if (fd >= 0)
Manager *m,
const char *name,
const char* old_owner,
const char *new_owner) {
Unit *u;
assert(m);
Manager *m,
const char *name,
Unit *u;
assert(m);
int fd;
FILE *f;
if (!path)
return -ENOMEM;
if (fd < 0) {
return -errno;
return -errno;
*_f = f;
Iterator i;
Unit *u;
assert(m);
assert(f);
if (!unit_can_serialize(u))
if (ferror(f))
return -EIO;
assert(m);
assert(f);
m->n_deserializing ++;
if (feof(f))
r = -errno;
goto finish;
Unit *u;
if (feof(f))
r = -errno;
goto finish;
goto finish;
goto finish;
if (ferror(f)) {
r = -EIO;
goto finish;
m->n_deserializing --;
FILE *f;
assert(m);
if ((r = manager_open_serialization(m, &f)) < 0)
r = -ENOMEM;
goto finish;
goto finish;
r = -errno;
goto finish;
m->n_deserializing ++;
if ((q = manager_enumerate(m)) < 0)
fclose(f);
f = NULL;
if ((q = manager_coldplug(m)) < 0)
m->n_deserializing ++;
fclose(f);
if (fds)
Unit *u;
assert(m);
Unit *u;
Iterator i;
assert(m);
Unit *u;
assert(m);
return unit_pending_inactive(u);
char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
assert(m);
const char *generator_path;
assert(m);
if (!m->generator_unit_path) {
goto finish;
goto finish;
goto finish;
goto finish;
closedir(d);
assert(m);
if (!m->generator_unit_path)
assert(m);
return -ENOMEM;
m->default_controllers = l;
Unit *u;
assert(m);
log_open();