#include <libcgroup.h>
#include <termios.h>
#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include "manager.h"
#include "hashmap.h"
#include "cgroup.h"
#include "mount-setup.h"
#include "utmp-wtmp.h"
+#include "unit-name.h"
+#include "dbus-unit.h"
+#include "dbus-job.h"
+#include "missing.h"
+
+/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
+#define GC_QUEUE_ENTRIES_MAX 16
+
+/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
+#define GC_QUEUE_USEC_MAX (5*USEC_PER_SEC)
static int enable_special_signals(Manager *m) {
char fd;
static int manager_setup_signals(Manager *m) {
sigset_t mask;
struct epoll_event ev;
+ struct sigaction sa;
assert(m);
+ /* We are not interested in SIGSTOP and friends. */
+ zero(sa);
+ sa.sa_handler = SIG_DFL;
+ sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
+ assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
+
assert_se(sigemptyset(&mask) == 0);
assert_se(sigaddset(&mask, SIGCHLD) == 0);
- assert_se(sigaddset(&mask, SIGINT) == 0); /* Kernel sends us this on control-alt-del */
- assert_se(sigaddset(&mask, SIGWINCH) == 0); /* Kernel sends us this on kbrequest (alt-arrowup) */
assert_se(sigaddset(&mask, SIGTERM) == 0);
assert_se(sigaddset(&mask, SIGHUP) == 0);
assert_se(sigaddset(&mask, SIGUSR1) == 0);
assert_se(sigaddset(&mask, SIGUSR2) == 0);
- assert_se(sigaddset(&mask, SIGPWR) == 0);
+ assert_se(sigaddset(&mask, SIGINT) == 0); /* Kernel sends us this on control-alt-del */
+ assert_se(sigaddset(&mask, SIGWINCH) == 0); /* Kernel sends us this on kbrequest (alt-arrowup) */
+ assert_se(sigaddset(&mask, SIGPWR) == 0); /* Some kernel drivers and upsd send us this on power failure */
assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
m->signal_watch.type = WATCH_SIGNAL;
}
if (m->running_as == MANAGER_INIT) {
- /* /etc/init.d/ compativility does not matter to users */
+ /* /etc/init.d/ compatibility does not matter to users */
if ((e = getenv("SYSTEMD_SYSVINIT_PATH")))
if (!(m->sysvinit_path = split_path_and_make_absolute(e)))
m->running_as = running_as;
m->confirm_spawn = confirm_spawn;
+ m->name_data_slot = -1;
+ m->exit_code = _MANAGER_EXIT_CODE_INVALID;
- m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1;
+ m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = -1;
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
+ if (!(m->environment = strv_copy(environ)))
+ goto fail;
+
if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
goto fail;
if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func)))
goto fail;
+ if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func)))
+ goto fail;
+
if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
goto fail;
return n;
}
-void manager_free(Manager *m) {
- UnitType c;
- Unit *u;
+enum {
+ GC_OFFSET_IN_PATH, /* This one is on the path we were travelling */
+ GC_OFFSET_UNSURE, /* No clue */
+ GC_OFFSET_GOOD, /* We still need this unit */
+ GC_OFFSET_BAD, /* We don't need this unit anymore */
+ _GC_OFFSET_MAX
+};
+
+static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
+ Iterator i;
+ Unit *other;
+ bool is_bad;
+
+ assert(u);
+
+ if (u->meta.gc_marker == gc_marker + GC_OFFSET_GOOD ||
+ u->meta.gc_marker == gc_marker + GC_OFFSET_BAD ||
+ u->meta.gc_marker == gc_marker + GC_OFFSET_IN_PATH)
+ return;
+
+ if (u->meta.in_cleanup_queue)
+ goto bad;
+
+ if (unit_check_gc(u))
+ goto good;
+
+ u->meta.gc_marker = gc_marker + GC_OFFSET_IN_PATH;
+
+ is_bad = true;
+
+ SET_FOREACH(other, u->meta.dependencies[UNIT_REFERENCED_BY], i) {
+ unit_gc_sweep(other, gc_marker);
+
+ if (other->meta.gc_marker == gc_marker + GC_OFFSET_GOOD)
+ goto good;
+
+ if (other->meta.gc_marker != gc_marker + GC_OFFSET_BAD)
+ is_bad = false;
+ }
+
+ if (is_bad)
+ goto bad;
+
+ /* We were unable to find anything out about this entry, so
+ * let's investigate it later */
+ u->meta.gc_marker = gc_marker + GC_OFFSET_UNSURE;
+ unit_add_to_gc_queue(u);
+ return;
+
+bad:
+ /* We definitely know that this one is not useful anymore, so
+ * let's mark it for deletion */
+ u->meta.gc_marker = gc_marker + GC_OFFSET_BAD;
+ unit_add_to_cleanup_queue(u);
+ return;
+
+good:
+ u->meta.gc_marker = gc_marker + GC_OFFSET_GOOD;
+}
+
+static unsigned manager_dispatch_gc_queue(Manager *m) {
+ Meta *meta;
+ unsigned n = 0;
+ unsigned gc_marker;
+
+ assert(m);
+
+ if ((m->n_in_gc_queue < GC_QUEUE_ENTRIES_MAX) &&
+ (m->gc_queue_timestamp <= 0 ||
+ (m->gc_queue_timestamp + GC_QUEUE_USEC_MAX) > now(CLOCK_MONOTONIC)))
+ return 0;
+
+ log_debug("Running GC...");
+
+ m->gc_marker += _GC_OFFSET_MAX;
+ if (m->gc_marker + _GC_OFFSET_MAX <= _GC_OFFSET_MAX)
+ m->gc_marker = 1;
+
+ gc_marker = m->gc_marker;
+
+ while ((meta = m->gc_queue)) {
+ assert(meta->in_gc_queue);
+
+ unit_gc_sweep(UNIT(meta), gc_marker);
+
+ LIST_REMOVE(Meta, gc_queue, m->gc_queue, meta);
+ meta->in_gc_queue = false;
+
+ n++;
+
+ if (meta->gc_marker == gc_marker + GC_OFFSET_BAD ||
+ meta->gc_marker == gc_marker + GC_OFFSET_UNSURE) {
+ log_debug("Collecting %s", meta->id);
+ meta->gc_marker = gc_marker + GC_OFFSET_BAD;
+ unit_add_to_cleanup_queue(UNIT(meta));
+ }
+ }
+
+ m->n_in_gc_queue = 0;
+ m->gc_queue_timestamp = 0;
+
+ return n;
+}
+
+static void manager_clear_jobs_and_units(Manager *m) {
Job *j;
+ Unit *u;
assert(m);
while ((u = hashmap_first(m->units)))
unit_free(u);
+}
+
+void manager_free(Manager *m) {
+ UnitType c;
+
+ assert(m);
manager_dispatch_cleanup_queue(m);
+ manager_clear_jobs_and_units(m);
for (c = 0; c < _UNIT_TYPE_MAX; c++)
if (unit_vtable[c]->shutdown)
unit_vtable[c]->shutdown(m);
- manager_shutdown_cgroup(m);
+ /* If we reexecute ourselves, we keep the root cgroup
+ * around */
+ manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
bus_done_api(m);
bus_done_system(m);
hashmap_free(m->jobs);
hashmap_free(m->transaction_jobs);
hashmap_free(m->watch_pids);
+ hashmap_free(m->watch_bus);
if (m->epoll_fd >= 0)
- close_nointr(m->epoll_fd);
+ close_nointr_nofail(m->epoll_fd);
if (m->signal_watch.fd >= 0)
- close_nointr(m->signal_watch.fd);
+ close_nointr_nofail(m->signal_watch.fd);
strv_free(m->unit_path);
strv_free(m->sysvinit_path);
strv_free(m->sysvrcnd_path);
+ strv_free(m->environment);
free(m->cgroup_controller);
free(m->cgroup_hierarchy);
- assert(hashmap_isempty(m->cgroup_bondings));
hashmap_free(m->cgroup_bondings);
free(m);
}
-int manager_coldplug(Manager *m) {
- int r;
+int manager_enumerate(Manager *m) {
+ int r = 0, q;
UnitType c;
- Iterator i;
- Unit *u;
- char *k;
assert(m);
- /* First, let's ask every type to load all units from
- * disk/kernel that it might know */
+ /* Let's ask every type to load all units from disk/kernel
+ * that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++)
if (unit_vtable[c]->enumerate)
- if ((r = unit_vtable[c]->enumerate(m)) < 0)
- return r;
+ if ((q = unit_vtable[c]->enumerate(m)) < 0)
+ r = q;
manager_dispatch_load_queue(m);
+ return r;
+}
+
+int manager_coldplug(Manager *m) {
+ int r = 0, q;
+ Iterator i;
+ Unit *u;
+ char *k;
+
+ assert(m);
/* Then, let's set up their initial state. */
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
/* ignore aliases */
- if (unit_id(u) != k)
+ if (u->meta.id != k)
continue;
if (UNIT_VTABLE(u)->coldplug)
- if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
- return r;
+ if ((q = UNIT_VTABLE(u)->coldplug(u)) < 0)
+ r = q;
}
+ return r;
+}
+
+int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
+ int r, q;
+
+ assert(m);
+
+ /* First, enumerate what we can from all config files */
+ r = manager_enumerate(m);
+
+ /* Second, deserialize if there is something to deserialize */
+ if (serialization)
+ if ((q = manager_deserialize(m, serialization, fds)) < 0)
+ r = q;
+
+ /* Third, fire things up! */
+ if ((q = manager_coldplug(m)) < 0)
+ r = q;
+
/* Now that the initial devices are available, let's see if we
* can write the utmp file */
manager_write_utmp_reboot(m);
- return 0;
+ return r;
}
static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) {
j->type = t;
j->state = JOB_WAITING;
- j->forced = j->forced || other->forced;
+ j->override = j->override || other->override;
j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
return -ENOEXEC;
/* Ok, we can drop one, so let's do so. */
- log_debug("Trying to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type));
+ log_debug("Trying to fix job merging by deleting job %s/%s", d->unit->meta.id, job_type_to_string(d->type));
transaction_delete_job(m, d, true);
return 0;
}
if (changes_something)
continue;
- log_debug("Found redundant job %s/%s, dropping.", unit_id(j->unit), job_type_to_string(j->type));
+ 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;
break;
* since smart how we are we stored our way back in
* there. */
- log_debug("Found ordering cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type));
+ log_debug("Found ordering cycle on %s/%s", j->unit->meta.id, job_type_to_string(j->type));
for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) {
- log_debug("Walked on cycle path to %s/%s", unit_id(k->unit), job_type_to_string(k->type));
+ log_debug("Walked on cycle path to %s/%s", k->unit->meta.id, job_type_to_string(k->type));
if (!k->installed &&
!unit_matters_to_anchor(k->unit, k)) {
/* Ok, we can drop this one, so let's
* do so. */
- log_debug("Breaking order cycle by deleting job %s/%s", unit_id(k->unit), job_type_to_string(k->type));
+ log_debug("Breaking order cycle by deleting job %s/%s", k->unit->meta.id, job_type_to_string(k->type));
transaction_delete_unit(m, k->unit);
return -EAGAIN;
}
if (j->object_list)
continue;
- log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type));
+ log_debug("Garbage collecting job %s/%s", j->unit->meta.id, job_type_to_string(j->type));
transaction_delete_job(m, j, true);
again = true;
break;
} while (again);
}
-static int transaction_is_destructive(Manager *m, JobMode mode) {
+static int transaction_is_destructive(Manager *m) {
Iterator i;
Job *j;
continue;
if (stops_running_service)
- log_debug("%s/%s would stop a running service.", unit_id(j->unit), job_type_to_string(j->type));
+ log_debug("%s/%s would stop a running service.", j->unit->meta.id, job_type_to_string(j->type));
if (changes_existing_job)
- log_debug("%s/%s would change existing job.", unit_id(j->unit), job_type_to_string(j->type));
+ log_debug("%s/%s would change existing job.", j->unit->meta.id, job_type_to_string(j->type));
/* Ok, let's get rid of this */
- log_debug("Deleting %s/%s to minimize impact.", unit_id(j->unit), job_type_to_string(j->type));
+ log_debug("Deleting %s/%s to minimize impact.", j->unit->meta.id, job_type_to_string(j->type));
transaction_delete_job(m, j, true);
again = true;
} while (again);
}
-static int transaction_apply(Manager *m, JobMode mode) {
+static int transaction_apply(Manager *m) {
Iterator i;
Job *j;
int r;
/* Ninth step: check whether we can actually apply this */
if (mode == JOB_FAIL)
- if ((r = transaction_is_destructive(m, mode)) < 0) {
+ if ((r = transaction_is_destructive(m)) < 0) {
log_debug("Requested transaction contradicts existing jobs: %s", strerror(-r));
goto rollback;
}
/* Tenth step: apply changes */
- if ((r = transaction_apply(m, mode)) < 0) {
+ if ((r = transaction_apply(m)) < 0) {
log_debug("Failed to apply transaction: %s", strerror(-r));
goto rollback;
}
return r;
}
-static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool force, bool *is_new) {
+static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool override, bool *is_new) {
Job *j, *f;
int r;
j->generation = 0;
j->marker = NULL;
j->matters_to_anchor = false;
- j->forced = force;
+ j->override = override;
LIST_PREPEND(Job, transaction, f, j);
if (is_new)
*is_new = true;
- log_debug("Added job %s/%s to transaction.", unit_id(unit), job_type_to_string(type));
+ log_debug("Added job %s/%s to transaction.", unit->meta.id, job_type_to_string(type));
return j;
}
if (other && delete_dependencies) {
log_debug("Deleting job %s/%s as dependency of job %s/%s",
- unit_id(other->unit), job_type_to_string(other->type),
- unit_id(j->unit), job_type_to_string(j->type));
+ other->unit->meta.id, job_type_to_string(other->type),
+ j->unit->meta.id, job_type_to_string(j->type));
transaction_delete_job(m, other, delete_dependencies);
}
}
}
-static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit *unit, Job *by, bool matters, bool force, Job **_ret) {
+static int transaction_add_job_and_dependencies(
+ Manager *m,
+ JobType type,
+ Unit *unit,
+ Job *by,
+ bool matters,
+ bool override,
+ Job **_ret) {
Job *ret;
Iterator i;
Unit *dep;
return -EBADR;
/* First add the job. */
- if (!(ret = transaction_add_one_job(m, type, unit, force, &is_new)))
+ if (!(ret = transaction_add_one_job(m, type, unit, override, &is_new)))
return -ENOMEM;
/* Then, add a link to the job. */
/* Finally, recursively add in all dependencies. */
if (type == JOB_START || type == JOB_RELOAD_OR_START) {
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
- goto fail;
- SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUIRES], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0 && r != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, NULL)) < 0 && r != -EBADR)
goto fail;
+
+ SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, NULL)) < 0 && r != -EBADR)
+ log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, strerror(-r));
+
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0)
- log_warning("Cannot add dependency job for unit %s, ignoring: %s", unit_id(dep), strerror(-r));
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, NULL)) < 0)
+ log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, strerror(-r));
+
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
- goto fail;
- SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUISITE], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) < 0 && r != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, NULL)) < 0 && r != -EBADR)
goto fail;
+
+ SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, NULL)) < 0 && r != -EBADR)
+ log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, strerror(-r));
+
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTS], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, NULL)) < 0 && r != -EBADR)
goto fail;
} else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRED_BY], i)
- if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, NULL)) < 0 && r != -EBADR)
goto fail;
}
return r;
}
-int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret) {
+static int transaction_add_isolate_jobs(Manager *m) {
+ Iterator i;
+ Unit *u;
+ char *k;
+ int r;
+
+ assert(m);
+
+ HASHMAP_FOREACH_KEY(u, k, m->units, i) {
+
+ /* ignore aliases */
+ if (u->meta.id != k)
+ continue;
+
+ if (UNIT_VTABLE(u)->no_isolate)
+ continue;
+
+ /* No need to stop inactive jobs */
+ if (unit_active_state(u) == UNIT_INACTIVE)
+ continue;
+
+ /* Is there already something listed for this? */
+ if (hashmap_get(m->transaction_jobs, u))
+ continue;
+
+ if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, u, NULL, true, false, NULL)) < 0)
+ log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->meta.id, strerror(-r));
+ }
+
+ return 0;
+}
+
+int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, Job **_ret) {
int r;
Job *ret;
assert(unit);
assert(mode < _JOB_MODE_MAX);
- log_debug("Trying to enqueue job %s/%s", unit_id(unit), job_type_to_string(type));
+ if (mode == JOB_ISOLATE && type != JOB_START)
+ return -EINVAL;
+
+ log_debug("Trying to enqueue job %s/%s", unit->meta.id, job_type_to_string(type));
- if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret)) < 0) {
+ if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, &ret)) < 0) {
transaction_abort(m);
return r;
}
+ if (mode == JOB_ISOLATE)
+ if ((r = transaction_add_isolate_jobs(m)) < 0) {
+ transaction_abort(m);
+ return r;
+ }
+
if ((r = transaction_activate(m, mode)) < 0)
return r;
- log_debug("Enqueued job %s/%s as %u", unit_id(unit), job_type_to_string(type), (unsigned) ret->id);
+ log_debug("Enqueued job %s/%s as %u", unit->meta.id, job_type_to_string(type), (unsigned) ret->id);
if (_ret)
*_ret = ret;
return 0;
}
-int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, Job **_ret) {
+int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool override, Job **_ret) {
Unit *unit;
int r;
assert(name);
assert(mode < _JOB_MODE_MAX);
- if ((r = manager_load_unit(m, name, &unit)) < 0)
+ if ((r = manager_load_unit(m, name, NULL, &unit)) < 0)
return r;
- return manager_add_job(m, type, unit, mode, force, _ret);
+ return manager_add_job(m, type, unit, mode, override, _ret);
}
Job *manager_get_job(Manager *m, uint32_t id) {
return n;
}
-int manager_load_unit(Manager *m, const char *path, Unit **_ret) {
+int manager_load_unit_prepare(Manager *m, const char *name, const char *path, Unit **_ret) {
Unit *ret;
int r;
- const char *name;
assert(m);
- assert(path);
- assert(_ret);
+ assert(name || path);
- /* This will load the service information files, but not actually
- * start any services or anything. */
+ /* This will prepare the unit for loading, but not actually
+ * load anything from disk. */
- name = file_name_from_path(path);
+ if (path && !is_path(path))
+ return -EINVAL;
+
+ if (!name)
+ name = file_name_from_path(path);
+
+ if (!unit_name_is_valid(name))
+ return -EINVAL;
if ((ret = manager_get_unit(m, name))) {
*_ret = ret;
if (!(ret = unit_new(m)))
return -ENOMEM;
- if (is_path(path)) {
+ if (path)
if (!(ret->meta.fragment_path = strdup(path))) {
unit_free(ret);
return -ENOMEM;
}
- }
if ((r = unit_add_name(ret, name)) < 0) {
unit_free(ret);
unit_add_to_load_queue(ret);
unit_add_to_dbus_queue(ret);
+ if (_ret)
+ *_ret = ret;
+
+ return 0;
+}
+
+int manager_load_unit(Manager *m, const char *name, const char *path, Unit **_ret) {
+ Unit *ret;
+ int r;
+
+ assert(m);
+
+ /* This will load the service information files, but not actually
+ * start any services or anything. */
+
+ if ((r = manager_load_unit_prepare(m, name, path, &ret)) < 0)
+ return r;
+
manager_dispatch_load_queue(m);
- *_ret = unit_follow_merge(ret);
+ if (_ret)
+ *_ret = unit_follow_merge(ret);
+
return 0;
}
assert(f);
HASHMAP_FOREACH_KEY(u, t, s->units, i)
- if (unit_id(u) == t)
+ if (u->meta.id == t)
unit_dump(u, f, prefix);
}
if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
continue;
- log_debug("Child %llu belongs to %s", (long long unsigned) si.si_pid, unit_id(u));
+ log_debug("Child %llu belongs to %s", (long long unsigned) si.si_pid, u->meta.id);
UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status);
}
log_error("Failed to enqueue %s job: %s", name, strerror(-r));
}
-static int manager_process_signal_fd(Manager *m, bool *quit) {
+static int manager_process_signal_fd(Manager *m) {
ssize_t n;
struct signalfd_siginfo sfsi;
bool sigchld = false;
sigchld = true;
break;
- case SIGINT:
case SIGTERM:
+ if (m->running_as == MANAGER_INIT)
+ /* This is for compatibility with the
+ * original sysvinit */
+ m->exit_code = MANAGER_REEXECUTE;
+ else
+ m->exit_code = MANAGER_EXIT;
+ return 0;
+
+ case SIGINT:
if (m->running_as == MANAGER_INIT) {
manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET);
break;
}
- *quit = true;
+ m->exit_code = MANAGER_EXIT;
return 0;
case SIGWINCH:
-
if (m->running_as == MANAGER_INIT)
manager_start_target(m, SPECIAL_KBREQUEST_TARGET);
/* This is a nop on non-init */
break;
- case SIGUSR1:
+ case SIGUSR1: {
+ Unit *u;
+
+ u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
+
+ if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
+ log_info("Trying to reconnect to bus...");
+ bus_init_system(m);
+ bus_init_api(m);
+ }
+
+ if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) {
+ log_info("Loading D-Bus service...");
+ manager_start_target(m, SPECIAL_DBUS_SERVICE);
+ }
+
+ break;
+ }
+
+ case SIGUSR2:
manager_dump_units(m, stdout, "\t");
manager_dump_jobs(m, stdout, "\t");
break;
+ case SIGHUP:
+ m->exit_code = MANAGER_RELOAD;
+ break;
+
default:
log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo));
}
return 0;
}
-static int process_event(Manager *m, struct epoll_event *ev, bool *quit) {
+static int process_event(Manager *m, struct epoll_event *ev) {
int r;
Watch *w;
if (ev->events != EPOLLIN)
return -EINVAL;
- if ((r = manager_process_signal_fd(m, quit)) < 0)
+ if ((r = manager_process_signal_fd(m)) < 0)
return r;
break;
int manager_loop(Manager *m) {
int r;
- bool quit = false;
RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 1000);
assert(m);
+ m->exit_code = MANAGER_RUNNING;
- do {
+ while (m->exit_code == MANAGER_RUNNING) {
struct epoll_event event;
int n;
if (manager_dispatch_cleanup_queue(m) > 0)
continue;
+ if (manager_dispatch_gc_queue(m) > 0)
+ continue;
+
if (manager_dispatch_load_queue(m) > 0)
continue;
if ((n = epoll_wait(m->epoll_fd, &event, 1, -1)) < 0) {
- if (errno == -EINTR)
+ if (errno == EINTR)
continue;
return -errno;
assert(n == 1);
- if ((r = process_event(m, &event, &quit)) < 0)
+ if ((r = process_event(m, &event)) < 0)
return r;
- } while (!quit);
+ }
- return 0;
+ return m->exit_code;
}
int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {
}
}
+void manager_dispatch_bus_name_owner_changed(
+ Manager *m,
+ const char *name,
+ const char* old_owner,
+ const char *new_owner) {
+
+ Unit *u;
+
+ assert(m);
+ assert(name);
+
+ if (!(u = hashmap_get(m->watch_bus, name)))
+ return;
+
+ UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
+}
+
+void manager_dispatch_bus_query_pid_done(
+ Manager *m,
+ const char *name,
+ pid_t pid) {
+
+ Unit *u;
+
+ assert(m);
+ assert(name);
+ assert(pid >= 1);
+
+ if (!(u = hashmap_get(m->watch_bus, name)))
+ return;
+
+ UNIT_VTABLE(u)->bus_query_pid_done(u, name, pid);
+}
+
+int manager_open_serialization(FILE **_f) {
+ char *path;
+ mode_t saved_umask;
+ int fd;
+ FILE *f;
+
+ assert(_f);
+
+ if (asprintf(&path, "/dev/shm/systemd-%u.dump-XXXXXX", (unsigned) getpid()) < 0)
+ return -ENOMEM;
+
+ saved_umask = umask(0077);
+ fd = mkostemp(path, O_RDWR|O_CLOEXEC);
+ umask(saved_umask);
+
+ if (fd < 0) {
+ free(path);
+ return -errno;
+ }
+
+ unlink(path);
+
+ log_debug("Serializing state to %s", path);
+ free(path);
+
+ if (!(f = fdopen(fd, "w+")) < 0)
+ return -errno;
+
+ *_f = f;
+
+ return 0;
+}
+
+int manager_serialize(Manager *m, FILE *f, FDSet *fds) {
+ Iterator i;
+ Unit *u;
+ const char *t;
+ int r;
+
+ assert(m);
+ assert(f);
+ assert(fds);
+
+ HASHMAP_FOREACH_KEY(u, t, m->units, i) {
+ if (u->meta.id != t)
+ continue;
+
+ if (!unit_can_serialize(u))
+ continue;
+
+ /* Start marker */
+ fputs(u->meta.id, f);
+ fputc('\n', f);
+
+ if ((r = unit_serialize(u, f, fds)) < 0)
+ return r;
+ }
+
+ if (ferror(f))
+ return -EIO;
+
+ return 0;
+}
+
+int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
+ int r = 0;
+
+ assert(m);
+ assert(f);
+
+ log_debug("Deserializing state...");
+
+ for (;;) {
+ Unit *u;
+ char name[UNIT_NAME_MAX+2];
+
+ /* Start marker */
+ if (!fgets(name, sizeof(name), f)) {
+ if (feof(f))
+ break;
+
+ return -errno;
+ }
+
+ char_array_0(name);
+
+ if ((r = manager_load_unit(m, strstrip(name), NULL, &u)) < 0)
+ return r;
+
+ if ((r = unit_deserialize(u, f, fds)) < 0)
+ return r;
+ }
+
+ if (ferror(f))
+ return -EIO;
+
+ return 0;
+}
+
+int manager_reload(Manager *m) {
+ int r, q;
+ FILE *f;
+ FDSet *fds;
+
+ assert(m);
+
+ if ((r = manager_open_serialization(&f)) < 0)
+ return r;
+
+ if (!(fds = fdset_new())) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if ((r = manager_serialize(m, f, fds)) < 0)
+ goto finish;
+
+ if (fseeko(f, 0, SEEK_SET) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* From here on there is no way back. */
+ manager_clear_jobs_and_units(m);
+
+ /* First, enumerate what we can from all config files */
+ if ((q = manager_enumerate(m)) < 0)
+ r = q;
+
+ /* Second, deserialize our stored data */
+ if ((q = manager_deserialize(m, f, fds)) < 0)
+ r = q;
+
+ fclose(f);
+ f = NULL;
+
+ /* Third, fire things up! */
+ if ((q = manager_coldplug(m)) < 0)
+ r = q;
+
+finish:
+ if (f)
+ fclose(f);
+
+ if (fds)
+ fdset_free(fds);
+
+ return r;
+}
+
static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
[MANAGER_INIT] = "init",
[MANAGER_SYSTEM] = "system",