rework merging/loading logic

This commit is contained in:
Lennart Poettering 2010-04-06 02:43:58 +02:00
parent 5af98f8284
commit 23a177ef7c
15 changed files with 629 additions and 274 deletions

View File

@ -26,7 +26,7 @@
#include "load-fragment.h"
#include "load-dropin.h"
static int automount_init(Unit *u) {
static int automount_init(Unit *u, UnitLoadState *new_state) {
int r;
Automount *a = AUTOMOUNT(u);
@ -35,13 +35,28 @@ static int automount_init(Unit *u) {
exec_context_init(&a->exec_context);
/* Load a .automount file */
if ((r = unit_load_fragment(u)) < 0)
if ((r = unit_load_fragment(u, new_state)) < 0)
return r;
if (*new_state == UNIT_STUB)
*new_state = UNIT_LOADED;
/* Load drop-in directory data */
if ((r = unit_load_dropin(u)) < 0)
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
return r;
if (*new_state == UNIT_LOADED) {
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount))) < 0)
return r;
if ((r = unit_add_exec_dependencies(u, &a->exec_context)) < 0)
return r;
if ((r = unit_add_default_cgroup(u)) < 0)
return r;
}
return 0;
}

View File

@ -381,7 +381,7 @@ fail:
const UnitVTable device_vtable = {
.suffix = ".device",
.init = unit_load_fragment_and_dropin,
.init = unit_load_fragment_and_dropin_optional,
.done = device_done,
.coldplug = device_coldplug,

2
job.c
View File

@ -65,7 +65,7 @@ void job_free(Job *j) {
}
/* Detach from next 'smaller' objects */
manager_transaction_unlink_job(j->manager, j);
manager_transaction_unlink_job(j->manager, j, true);
if (j->in_run_queue)
LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);

View File

@ -95,36 +95,15 @@ static int config_parse_names(
FOREACH_WORD(w, l, rvalue, state) {
char *t;
int r;
Unit *other;
if (!(t = strndup(w, l)))
return -ENOMEM;
other = manager_get_unit(u->meta.manager, t);
if (other) {
if (other != u) {
if (other->meta.load_state != UNIT_STUB) {
free(t);
return -EEXIST;
}
if ((r = unit_merge(u, other)) < 0) {
free(t);
return r;
}
}
} else {
if ((r = unit_add_name(u, t)) < 0) {
free(t);
return r;
}
}
r = unit_merge_by_name(u, t);
free(t);
if (r < 0)
return r;
}
return 0;
@ -1070,7 +1049,48 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_id) {
return 0;
}
static int load_from_path(Unit *u, const char *path) {
static int merge_by_names(Unit **u, Set *names, const char *id) {
char *k;
int r;
assert(u);
assert(*u);
assert(names);
/* Let's try to add in all symlink names we found */
while ((k = set_steal_first(names))) {
/* First try to merge in the other name into our
* unit */
if ((r = unit_merge_by_name(*u, k)) < 0) {
Unit *other;
/* Hmm, we couldn't merge the other unit into
* ours? Then let's try it the other way
* round */
other = manager_get_unit((*u)->meta.manager, k);
free(k);
if (other)
if ((r = unit_merge(other, *u)) >= 0) {
*u = other;
return merge_by_names(u, names, NULL);
}
return r;
}
if (id == k)
unit_choose_id(*u, id);
free(k);
}
return 0;
}
static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
static const char* const section_table[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = "Service",
@ -1184,8 +1204,12 @@ static int load_from_path(Unit *u, const char *path) {
char *k;
int r;
Set *symlink_names;
FILE *f;
char *filename = NULL, *id;
FILE *f = NULL;
char *filename = NULL, *id = NULL;
Unit *merged;
assert(u);
assert(new_state);
sections[0] = "Meta";
sections[1] = section_table[u->meta.type];
@ -1243,90 +1267,79 @@ static int load_from_path(Unit *u, const char *path) {
}
if (!filename) {
r = 0; /* returning 0 means: no suitable config file found */
r = 0;
goto finish;
}
merged = u;
if ((r = merge_by_names(&merged, symlink_names, id)) < 0)
goto finish;
if (merged != u) {
*new_state = UNIT_MERGED;
r = 0;
goto finish;
}
/* Now, parse the file contents */
r = config_parse(filename, f, sections, items, u);
if (r < 0)
if ((r = config_parse(filename, f, sections, items, u)) < 0)
goto finish;
/* Let's try to add in all symlink names we found */
while ((k = set_steal_first(symlink_names))) {
if ((r = unit_add_name(u, k)) < 0)
goto finish;
if (id == k)
unit_choose_id(u, id);
free(k);
}
free(u->meta.fragment_path);
u->meta.fragment_path = filename;
filename = NULL;
r = 1; /* returning 1 means: suitable config file found and loaded */
*new_state = UNIT_LOADED;
r = 0;
finish:
while ((k = set_steal_first(symlink_names)))
free(k);
set_free(symlink_names);
free(filename);
if (f)
fclose(f);
return r;
}
int unit_load_fragment(Unit *u) {
int r = 0;
int unit_load_fragment(Unit *u, UnitLoadState *new_state) {
int r;
assert(u);
assert(u->meta.load_state == UNIT_STUB);
assert(new_state);
assert(*new_state == UNIT_STUB);
if (u->meta.fragment_path)
r = load_from_path(u, u->meta.fragment_path);
else {
if (u->meta.fragment_path) {
if ((r = load_from_path(u, u->meta.fragment_path, new_state)) < 0)
return r;
} else {
Iterator i;
const char *t;
/* Try to find the unit under its id */
if ((t = unit_id(u)))
r = load_from_path(u, t);
if ((r = load_from_path(u, t, new_state)) < 0)
return r;
/* Try to find an alias we can load this with */
if (r == 0)
SET_FOREACH(t, u->meta.names, i)
if ((r = load_from_path(u, t)) != 0)
if (*new_state == UNIT_STUB)
SET_FOREACH(t, u->meta.names, i) {
if (unit_id(u) == t)
continue;
if ((r = load_from_path(u, t, new_state)) < 0)
return r;
if (*new_state != UNIT_STUB)
break;
}
}
if (r >= 0) {
ExecContext *c;
if (u->meta.type == UNIT_SOCKET)
c = &u->socket.exec_context;
else if (u->meta.type == UNIT_SERVICE)
c = &u->service.exec_context;
else
c = NULL;
if (c &&
(c->output == EXEC_OUTPUT_KERNEL || c->output == EXEC_OUTPUT_SYSLOG)) {
int k;
/* If syslog or kernel logging is requested, make sure
* our own logging daemon is run first. */
if ((k = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET)) < 0)
return k;
if (u->meta.manager->running_as != MANAGER_SESSION)
if ((k = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET)) < 0)
return k;
}
}
return r;
return 0;
}

View File

@ -26,6 +26,6 @@
/* Read service data from .desktop file style configuration fragments */
int unit_load_fragment(Unit *u);
int unit_load_fragment(Unit *u, UnitLoadState *new_state);
#endif

124
manager.c
View File

@ -194,6 +194,7 @@ fail:
static int manager_find_paths(Manager *m) {
const char *e;
char *t;
assert(m);
/* First priority is whatever has been passed to us via env
@ -326,6 +327,22 @@ fail:
return r;
}
static unsigned manager_dispatch_cleanup_queue(Manager *m) {
Meta *meta;
unsigned n = 0;
assert(m);
while ((meta = m->cleanup_queue)) {
assert(meta->in_cleanup_queue);
unit_free(UNIT(meta));
n++;
}
return n;
}
void manager_free(Manager *m) {
UnitType c;
Unit *u;
@ -339,6 +356,8 @@ void manager_free(Manager *m) {
while ((u = hashmap_first(m->units)))
unit_free(u);
manager_dispatch_cleanup_queue(m);
for (c = 0; c < _UNIT_TYPE_MAX; c++)
if (unit_vtable[c]->shutdown)
unit_vtable[c]->shutdown(m);
@ -402,13 +421,13 @@ int manager_coldplug(Manager *m) {
return 0;
}
static void transaction_delete_job(Manager *m, Job *j) {
static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) {
assert(m);
assert(j);
/* Deletes one job from the transaction */
manager_transaction_unlink_job(m, j);
manager_transaction_unlink_job(m, j, delete_dependencies);
if (!j->installed)
job_free(j);
@ -421,7 +440,7 @@ static void transaction_delete_unit(Manager *m, Unit *u) {
* transaction */
while ((j = hashmap_get(m->transaction_jobs, u)))
transaction_delete_job(m, j);
transaction_delete_job(m, j, true);
}
static void transaction_clean_dependencies(Manager *m) {
@ -449,7 +468,7 @@ static void transaction_abort(Manager *m) {
while ((j = hashmap_first(m->transaction_jobs)))
if (j->installed)
transaction_delete_job(m, j);
transaction_delete_job(m, j, true);
else
job_free(j);
@ -541,7 +560,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job
/* Kill the other job */
other->subject_list = NULL;
other->object_list = NULL;
transaction_delete_job(m, other);
transaction_delete_job(m, other, true);
}
static int delete_one_unmergeable_job(Manager *m, Job *j) {
@ -574,8 +593,8 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) {
return -ENOEXEC;
/* Ok, we can drop one, so let's do so. */
log_debug("Try to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type));
transaction_delete_job(m, d);
log_debug("Trying to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type));
transaction_delete_job(m, d, true);
return 0;
}
@ -643,6 +662,46 @@ static int transaction_merge_jobs(Manager *m) {
return 0;
}
static void transaction_drop_redundant(Manager *m) {
bool again;
assert(m);
/* Goes through the transaction and removes all jobs that are
* a noop */
do {
Job *j;
Iterator i;
again = false;
HASHMAP_FOREACH(j, m->transaction_jobs, i) {
bool changes_something = false;
Job *k;
LIST_FOREACH(transaction, k, j) {
if (!job_is_anchor(k) &&
job_type_is_redundant(k->type, unit_active_state(k->unit)))
continue;
changes_something = true;
break;
}
if (changes_something)
continue;
log_debug("Found redundant job %s/%s, dropping.", unit_id(j->unit), job_type_to_string(j->type));
transaction_delete_job(m, j, false);
again = true;
break;
}
} while (again);
}
static bool unit_matters_to_anchor(Unit *u, Job *j) {
assert(u);
assert(!j->transaction_prev);
@ -680,11 +739,11 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned
* since smart how we are we stored our way back in
* there. */
log_debug("Found cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type));
log_debug("Found ordering cycle on %s/%s", unit_id(j->unit), 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(j->unit), job_type_to_string(j->type));
log_debug("Walked on cycle path to %s/%s", unit_id(k->unit), job_type_to_string(k->type));
if (!k->installed &&
!unit_matters_to_anchor(k->unit, k)) {
@ -772,7 +831,7 @@ static void transaction_collect_garbage(Manager *m) {
continue;
log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type));
transaction_delete_job(m, j);
transaction_delete_job(m, j, true);
again = true;
break;
}
@ -847,7 +906,7 @@ static void transaction_minimize_impact(Manager *m) {
/* 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));
transaction_delete_job(m, j);
transaction_delete_job(m, j, true);
again = true;
break;
}
@ -932,12 +991,15 @@ static int transaction_activate(Manager *m, JobMode mode) {
* jobs if we don't have to. */
transaction_minimize_impact(m);
/* Third step: Drop redundant jobs */
transaction_drop_redundant(m);
for (;;) {
/* Third step: Let's remove unneeded jobs that might
/* Fourth step: Let's remove unneeded jobs that might
* be lurking. */
transaction_collect_garbage(m);
/* Fourth step: verify order makes sense and correct
/* Fifth step: verify order makes sense and correct
* cycles if necessary and possible */
if ((r = transaction_verify_order(m, &generation)) >= 0)
break;
@ -952,7 +1014,7 @@ static int transaction_activate(Manager *m, JobMode mode) {
}
for (;;) {
/* Fifth step: let's drop unmergeable entries if
/* Sixth step: let's drop unmergeable entries if
* necessary and possible, merge entries we can
* merge */
if ((r = transaction_merge_jobs(m)) >= 0)
@ -963,7 +1025,7 @@ static int transaction_activate(Manager *m, JobMode mode) {
goto rollback;
}
/* Sixth step: an entry got dropped, let's garbage
/* Seventh step: an entry got dropped, let's garbage
* collect its dependencies. */
transaction_collect_garbage(m);
@ -971,14 +1033,17 @@ static int transaction_activate(Manager *m, JobMode mode) {
* unmergeable entries ... */
}
/* Seventh step: check whether we can actually apply this */
/* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
transaction_drop_redundant(m);
/* Ninth step: check whether we can actually apply this */
if (mode == JOB_FAIL)
if ((r = transaction_is_destructive(m, mode)) < 0) {
log_debug("Requested transaction contradicts existing jobs: %s", strerror(-r));
goto rollback;
}
/* Eights step: apply changes */
/* Tenth step: apply changes */
if ((r = transaction_apply(m, mode)) < 0) {
log_debug("Failed to apply transaction: %s", strerror(-r));
goto rollback;
@ -1037,10 +1102,12 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool f
if (is_new)
*is_new = true;
log_debug("Added job %s/%s to transaction.", unit_id(unit), job_type_to_string(type));
return j;
}
void manager_transaction_unlink_job(Manager *m, Job *j) {
void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies) {
assert(m);
assert(j);
@ -1064,11 +1131,11 @@ void manager_transaction_unlink_job(Manager *m, Job *j) {
job_dependency_free(j->object_list);
if (other) {
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));
transaction_delete_job(m, other);
transaction_delete_job(m, other, delete_dependencies);
}
}
}
@ -1244,7 +1311,7 @@ int manager_load_unit(Manager *m, const char *path, Unit **_ret) {
manager_dispatch_load_queue(m);
*_ret = ret;
*_ret = unit_follow_merge(ret);
return 0;
}
@ -1317,10 +1384,9 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
m->dispatching_dbus_queue = true;
while ((meta = m->dbus_unit_queue)) {
Unit *u = (Unit*) meta;
assert(u->meta.in_dbus_queue);
assert(meta->in_dbus_queue);
bus_unit_send_change_signal(u);
bus_unit_send_change_signal(UNIT(meta));
n++;
}
@ -1521,7 +1587,7 @@ int manager_loop(Manager *m) {
assert(m);
for (;;) {
do {
struct epoll_event event;
int n;
@ -1531,6 +1597,9 @@ int manager_loop(Manager *m) {
sleep(1);
}
if (manager_dispatch_cleanup_queue(m) > 0)
continue;
if (manager_dispatch_load_queue(m) > 0)
continue;
@ -1555,10 +1624,9 @@ int manager_loop(Manager *m) {
if ((r = process_event(m, &event, &quit)) < 0)
return r;
} while (!quit);
if (quit)
return 0;
}
return 0;
}
int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {

View File

@ -125,6 +125,8 @@ struct Manager {
LIST_HEAD(Meta, dbus_unit_queue);
LIST_HEAD(Job, dbus_job_queue);
LIST_HEAD(Meta, cleanup_queue);
/* Jobs to be added */
Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */
JobDependency *transaction_anchor;
@ -181,7 +183,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for
void manager_dump_units(Manager *s, FILE *f, const char *prefix);
void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
void manager_transaction_unlink_job(Manager *m, Job *j);
void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies);
void manager_clear_jobs(Manager *m);

View File

@ -472,7 +472,7 @@ void mount_fd_event(Manager *m, int events) {
const UnitVTable mount_vtable = {
.suffix = ".mount",
.init = unit_load_fragment_and_dropin,
.init = unit_load_fragment_and_dropin_optional,
.done = mount_done,
.coldplug = mount_coldplug,

216
service.c
View File

@ -35,6 +35,17 @@
#define NEWLINES "\n\r"
#define LINE_MAX 4096
static const char * const rcnd_table[] = {
"../rc0.d", SPECIAL_RUNLEVEL0_TARGET,
"../rc1.d", SPECIAL_RUNLEVEL1_TARGET,
"../rc2.d", SPECIAL_RUNLEVEL2_TARGET,
"../rc3.d", SPECIAL_RUNLEVEL3_TARGET,
"../rc4.d", SPECIAL_RUNLEVEL4_TARGET,
"../rc5.d", SPECIAL_RUNLEVEL5_TARGET,
"../rc6.d", SPECIAL_RUNLEVEL6_TARGET
};
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_DEAD] = UNIT_INACTIVE,
[SERVICE_START_PRE] = UNIT_ACTIVATING,
@ -128,8 +139,8 @@ static int sysv_chkconfig_order(Service *s) {
if (s->sysv_start_priority < 0)
return 0;
/* If no LSB header is found we try to order init scripts via
* the start priority of the chkconfig header. */
/* For each pair of services where at least one lacks a LSB
* header, we use the start priority value to order things. */
LIST_FOREACH(units_per_type, other, UNIT(s)->meta.manager->units_per_type[UNIT_SERVICE]) {
Service *t;
@ -143,6 +154,9 @@ static int sysv_chkconfig_order(Service *s) {
if (t->sysv_start_priority < 0)
continue;
if (s->sysv_has_lsb && t->sysv_has_lsb)
continue;
if (t->sysv_start_priority < s->sysv_start_priority)
d = UNIT_AFTER;
else if (t->sysv_start_priority > s->sysv_start_priority)
@ -200,7 +214,67 @@ static int sysv_exec_commands(Service *s) {
return 0;
}
static int service_load_sysv_path(Service *s, const char *path) {
static int priority_from_rcd(Service *s, const char *init_script) {
char **p;
unsigned i;
STRV_FOREACH(p, UNIT(s)->meta.manager->sysvinit_path)
for (i = 0; i < ELEMENTSOF(rcnd_table); i += 2) {
char *path;
DIR *d;
struct dirent *de;
if (asprintf(&path, "%s/%s", *p, rcnd_table[i]) < 0)
return -ENOMEM;
d = opendir(path);
free(path);
if (!d) {
if (errno != ENOENT)
log_warning("opendir() failed on %s: %s", path, strerror(errno));
continue;
}
while ((de = readdir(d))) {
int a, b;
if (ignore_file(de->d_name))
continue;
if (de->d_name[0] != 'S')
continue;
if (strlen(de->d_name) < 4)
continue;
if (!streq(de->d_name + 3, init_script))
continue;
/* Yay, we found it! Now decode the priority */
a = undecchar(de->d_name[1]);
b = undecchar(de->d_name[2]);
if (a < 0 || b < 0)
continue;
s->sysv_start_priority = a*10 + b;
log_debug("Determined priority %i from link farm for %s", s->sysv_start_priority, unit_id(UNIT(s)));
closedir(d);
return 0;
}
closedir(d);
}
return 0;
}
static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *new_state) {
FILE *f;
Unit *u;
unsigned line = 0;
@ -211,7 +285,10 @@ static int service_load_sysv_path(Service *s, const char *path) {
LSB,
LSB_DESCRIPTION
} state = NORMAL;
bool has_lsb = false;
assert(s);
assert(path);
assert(new_state);
u = UNIT(s);
@ -249,7 +326,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
state = LSB;
has_lsb = true;
s->sysv_has_lsb = true;
continue;
}
@ -470,14 +547,21 @@ static int service_load_sysv_path(Service *s, const char *path) {
}
}
/* If the init script has no LSB header, then let's
* enforce the ordering via the chkconfig
* priorities */
/* If init scripts have no LSB header, then we enforce the
* ordering via the chkconfig priorities. We try to determine
* a priority for *all* init scripts here, since they are
* needed as soon as at least one non-LSB script is used. */
if (!has_lsb)
if ((r = sysv_chkconfig_order(s)) < 0)
if (s->sysv_start_priority < 0) {
log_debug("%s has no chkconfig header, trying to determine SysV priority from link farm.", unit_id(u));
if ((r = priority_from_rcd(s, file_name_from_path(path))) < 0)
goto finish;
if (s->sysv_start_priority < 0)
log_warning("%s has neither a chkconfig header nor a directory link, cannot order unit!", unit_id(u));
}
if ((r = sysv_exec_commands(s)) < 0)
goto finish;
@ -485,7 +569,8 @@ static int service_load_sysv_path(Service *s, const char *path) {
(r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_SYSINIT_SERVICE)) < 0)
goto finish;
r = 1;
*new_state = UNIT_LOADED;
r = 0;
finish:
@ -495,7 +580,7 @@ finish:
return r;
}
static int service_load_sysv_name(Service *s, const char *name) {
static int service_load_sysv_name(Service *s, const char *name, UnitLoadState *new_state) {
char **p;
assert(s);
@ -511,22 +596,26 @@ static int service_load_sysv_name(Service *s, const char *name) {
assert(endswith(path, ".service"));
path[strlen(path)-8] = 0;
r = service_load_sysv_path(s, path);
r = service_load_sysv_path(s, path, new_state);
free(path);
if (r != 0)
if (r < 0)
return r;
if (*new_state != UNIT_STUB)
break;
}
return 0;
}
static int service_load_sysv(Service *s) {
static int service_load_sysv(Service *s, UnitLoadState *new_state) {
const char *t;
Iterator i;
int r;
assert(s);
assert(new_state);
/* Load service data from SysV init scripts, preferably with
* LSB headers ... */
@ -535,21 +624,28 @@ static int service_load_sysv(Service *s) {
return 0;
if ((t = unit_id(UNIT(s))))
if ((r = service_load_sysv_name(s, t)) != 0)
if ((r = service_load_sysv_name(s, t, new_state)) < 0)
return r;
SET_FOREACH(t, UNIT(s)->meta.names, i)
if ((r == service_load_sysv_name(s, t)) != 0)
return r;
if (*new_state == UNIT_STUB)
SET_FOREACH(t, UNIT(s)->meta.names, i) {
if ((r == service_load_sysv_name(s, t, new_state)) < 0)
return r;
if (*new_state != UNIT_STUB)
break;
}
return 0;
}
static int service_init(Unit *u) {
static int service_init(Unit *u, UnitLoadState *new_state) {
int r;
Service *s = SERVICE(u);
assert(s);
assert(new_state);
assert(*new_state == UNIT_STUB);
/* First, reset everything to the defaults, in case this is a
* reload */
@ -570,31 +666,38 @@ static int service_init(Unit *u) {
s->permissions_start_only = false;
s->root_directory_start_only = false;
s->sysv_has_lsb = false;
RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
/* Load a .service file */
if ((r = unit_load_fragment(u)) < 0) {
service_done(u);
if ((r = unit_load_fragment(u, new_state)) < 0)
return r;
}
/* Load a classic init script as a fallback, if we couldn't find anything */
if (r == 0)
if ((r = service_load_sysv(s)) <= 0) {
service_done(u);
return r < 0 ? r : -ENOENT;
}
if (*new_state == UNIT_STUB)
if ((r = service_load_sysv(s, new_state)) < 0)
return r;
/* Load dropin directory data */
if ((r = unit_load_dropin(u)) < 0) {
service_done(u);
return r;
}
/* Still nothing found? Then let's give up */
if (*new_state == UNIT_STUB)
return -ENOENT;
/* Add default cgroup */
if ((r = unit_add_default_cgroup(u)) < 0) {
service_done(u);
/* We were able to load something, then let's add in the
* dropin directories. */
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
return r;
/* This is a new unit? Then let's add in some extras */
if (*new_state == UNIT_LOADED) {
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
return r;
if ((r = unit_add_default_cgroup(u)) < 0)
return r;
if ((r = sysv_chkconfig_order(s)) < 0)
return r;
}
return 0;
@ -645,14 +748,17 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
if (s->sysv_path)
fprintf(f,
"%sSysV Init Script Path: %s\n",
prefix, s->sysv_path);
"%sSysV Init Script Path: %s\n"
"%sSysV Init Script has LSB Header: %s\n",
prefix, s->sysv_path,
prefix, yes_no(s->sysv_has_lsb));
if (s->sysv_start_priority >= 0)
fprintf(f,
"%sSysV Start Priority: %i\n",
"%sSysVStartPriority: %i\n",
prefix, s->sysv_start_priority);
free(p2);
}
@ -1638,17 +1744,6 @@ static void service_cgroup_notify_event(Unit *u) {
}
static int service_enumerate(Manager *m) {
static const char * const rcnd[] = {
"../rc0.d", SPECIAL_RUNLEVEL0_TARGET,
"../rc1.d", SPECIAL_RUNLEVEL1_TARGET,
"../rc2.d", SPECIAL_RUNLEVEL2_TARGET,
"../rc3.d", SPECIAL_RUNLEVEL3_TARGET,
"../rc4.d", SPECIAL_RUNLEVEL4_TARGET,
"../rc5.d", SPECIAL_RUNLEVEL5_TARGET,
"../rc6.d", SPECIAL_RUNLEVEL6_TARGET
};
char **p;
unsigned i;
DIR *d = NULL;
@ -1658,12 +1753,12 @@ static int service_enumerate(Manager *m) {
assert(m);
STRV_FOREACH(p, m->sysvinit_path)
for (i = 0; i < ELEMENTSOF(rcnd); i += 2) {
for (i = 0; i < ELEMENTSOF(rcnd_table); i += 2) {
struct dirent *de;
free(path);
path = NULL;
if (asprintf(&path, "%s/%s", *p, rcnd[i]) < 0) {
if (asprintf(&path, "%s/%s", *p, rcnd_table[i]) < 0) {
r = -ENOMEM;
goto finish;
}
@ -1692,7 +1787,7 @@ static int service_enumerate(Manager *m) {
free(fpath);
fpath = NULL;
if (asprintf(&fpath, "%s/%s/%s", *p, rcnd[i], de->d_name) < 0) {
if (asprintf(&fpath, "%s/%s/%s", *p, rcnd_table[i], de->d_name) < 0) {
r = -ENOMEM;
goto finish;
}
@ -1715,7 +1810,7 @@ static int service_enumerate(Manager *m) {
if ((r = manager_load_unit(m, name, &service)) < 0)
goto finish;
if ((r = manager_load_unit(m, rcnd[i+1], &runlevel)) < 0)
if ((r = manager_load_unit(m, rcnd_table[i+1], &runlevel)) < 0)
goto finish;
if (de->d_name[0] == 'S') {
@ -1724,7 +1819,18 @@ static int service_enumerate(Manager *m) {
if ((r = unit_add_dependency(runlevel, UNIT_AFTER, service)) < 0)
goto finish;
} else {
} else if (de->d_name[0] == 'K' &&
(streq(rcnd_table[i+1], SPECIAL_RUNLEVEL0_TARGET) ||
streq(rcnd_table[i+1], SPECIAL_RUNLEVEL6_TARGET))) {
/* We honour K links only for
* halt/reboot. For the normal
* runlevels we assume the
* stop jobs will be
* implicitly added by the
* core logic. */
if ((r = unit_add_dependency(runlevel, UNIT_CONFLICTS, service)) < 0)
goto finish;

View File

@ -100,6 +100,8 @@ struct Service {
pid_t main_pid, control_pid;
bool main_pid_known:1;
bool sysv_has_lsb:1;
bool failure:1; /* if we shut down, remember why */
Watch timer_watch;

View File

@ -30,6 +30,8 @@
#include "unit.h"
#include "socket.h"
#include "log.h"
#include "load-dropin.h"
#include "load-fragment.h"
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = UNIT_INACTIVE,
@ -92,7 +94,7 @@ static void socket_done(Unit *u) {
unit_unwatch_timer(u, &s->timer_watch);
}
static int socket_init(Unit *u) {
static int socket_init(Unit *u, UnitLoadState *new_state) {
Socket *s = SOCKET(u);
char *t;
int r;
@ -109,35 +111,38 @@ static int socket_init(Unit *u) {
s->socket_mode = 0666;
exec_context_init(&s->exec_context);
if ((r = unit_load_fragment_and_dropin(u)) <= 0) {
if (r == 0)
r = -ENOENT;
goto fail;
if ((r = unit_load_fragment(u, new_state)) < 0)
return r;
if (*new_state == UNIT_STUB)
return -ENOENT;
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
return r;
/* This is a new unit? Then let's add in some extras */
if (*new_state == UNIT_LOADED) {
if (!(t = unit_name_change_suffix(unit_id(u), ".service")))
return -ENOMEM;
r = manager_load_unit(u->meta.manager, t, (Unit**) &s->service);
free(t);
if (r < 0)
return r;
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
return r;
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
return r;
if ((r = unit_add_default_cgroup(u)) < 0)
return r;
}
if (!(t = unit_name_change_suffix(unit_id(u), ".service"))) {
r = -ENOMEM;
goto fail;
}
r = manager_load_unit(u->meta.manager, t, (Unit**) &s->service);
free(t);
if (r < 0)
goto fail;
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
goto fail;
/* Add default cgroup */
if ((r = unit_add_default_cgroup(u)) < 0)
goto fail;
return 0;
fail:
socket_done(u);
return r;
}
static const char* listen_lookup(int type) {

View File

@ -20,6 +20,7 @@
***/
#include <errno.h>
#include <signal.h>
#include "unit.h"
#include "target.h"
@ -36,18 +37,6 @@ static const char* const state_string_table[_TARGET_STATE_MAX] = {
[TARGET_ACTIVE] = "active"
};
static int target_init(Unit *u) {
int r;
assert(u);
/* Make sure this config file actually exists */
if ((r = unit_load_fragment_and_dropin(u)) <= 0)
return r < 0 ? r : -ENOENT;
return 0;
}
static void target_dump(Unit *u, FILE *f, const char *prefix) {
Target *t = TARGET(u);
@ -100,7 +89,7 @@ static UnitActiveState target_active_state(Unit *u) {
const UnitVTable target_vtable = {
.suffix = ".target",
.init = target_init,
.init = unit_load_fragment_and_dropin,
.dump = target_dump,

15
timer.c
View File

@ -30,19 +30,6 @@ static void timer_done(Unit *u) {
assert(t);
}
static int timer_init(Unit *u) {
int r;
assert(u);
/* Make sure this config file actually exists */
if ((r = unit_load_fragment_and_dropin(u)) <= 0)
return r < 0 ? r : -ENOENT;
return 0;
}
static UnitActiveState timer_active_state(Unit *u) {
static const UnitActiveState table[_TIMER_STATE_MAX] = {
@ -57,7 +44,7 @@ static UnitActiveState timer_active_state(Unit *u) {
const UnitVTable timer_vtable = {
.suffix = ".timer",
.init = timer_init,
.init = unit_load_fragment_and_dropin,
.done = timer_done,
.active_state = timer_active_state

246
unit.c
View File

@ -220,6 +220,16 @@ void unit_add_to_load_queue(Unit *u) {
u->meta.in_load_queue = true;
}
void unit_add_to_cleanup_queue(Unit *u) {
assert(u);
if (u->meta.in_cleanup_queue)
return;
LIST_PREPEND(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta);
u->meta.in_cleanup_queue = true;
}
void unit_add_to_dbus_queue(Unit *u) {
assert(u);
@ -274,7 +284,10 @@ void unit_free(Unit *u) {
if (u->meta.in_dbus_queue)
LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta);
if (u->meta.load_state == UNIT_LOADED)
if (u->meta.in_cleanup_queue)
LIST_REMOVE(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta);
if (u->meta.load_state != UNIT_STUB)
if (UNIT_VTABLE(u)->done)
UNIT_VTABLE(u)->done(u);
@ -304,51 +317,145 @@ UnitActiveState unit_active_state(Unit *u) {
return UNIT_VTABLE(u)->active_state(u);
}
static int ensure_merge(Set **s, Set *other) {
static void complete_move(Set **s, Set **other) {
assert(s);
assert(other);
if (!other)
return 0;
if (!*other)
return;
if (*s)
return set_merge(*s, other);
if (!(*s = set_copy(other)))
return -ENOMEM;
return 0;
set_move(*s, *other);
else {
*s = *other;
*other = NULL;
}
}
/* FIXME: Does not rollback on failure! Needs to fix special unit
* pointers. Needs to merge names and dependencies properly.*/
int unit_merge(Unit *u, Unit *other) {
static void merge_names(Unit *u, Unit *other) {
char *t;
Iterator i;
assert(u);
assert(other);
complete_move(&u->meta.names, &other->meta.names);
while ((t = set_steal_first(other->meta.names)))
free(t);
set_free(other->meta.names);
other->meta.names = NULL;
other->meta.id = NULL;
SET_FOREACH(t, u->meta.names, i)
assert_se(hashmap_replace(u->meta.manager->units, t, u) == 0);
}
static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) {
Iterator i;
Unit *back;
int r;
assert(u);
assert(other);
assert(d < _UNIT_DEPENDENCY_MAX);
SET_FOREACH(back, other->meta.dependencies[d], i) {
UnitDependency k;
for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++)
if ((r = set_remove_and_put(back->meta.dependencies[k], other, u)) < 0) {
if (r == -EEXIST)
set_remove(back->meta.dependencies[k], other);
else
assert(r == -ENOENT);
}
}
complete_move(&u->meta.dependencies[d], &other->meta.dependencies[d]);
set_free(other->meta.dependencies[d]);
other->meta.dependencies[d] = NULL;
}
int unit_merge(Unit *u, Unit *other) {
UnitDependency d;
assert(u);
assert(other);
assert(u->meta.manager == other->meta.manager);
if (other == u)
return 0;
/* This merges 'other' into 'unit'. FIXME: This does not
* rollback on failure. */
if (u->meta.type != u->meta.type)
return -EINVAL;
if (u->meta.load_state != UNIT_STUB)
return -EINVAL;
if (other->meta.load_state != UNIT_STUB)
return -EEXIST;
/* Merge names */
if ((r = ensure_merge(&u->meta.names, other->meta.names)) < 0)
return r;
merge_names(u, other);
/* Merge dependencies */
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
/* fixme, the inverse mapping is missing */
if ((r = ensure_merge(&u->meta.dependencies[d], other->meta.dependencies[d])) < 0)
return r;
merge_dependencies(u, other, d);
unit_add_to_dbus_queue(u);
other->meta.load_state = UNIT_MERGED;
other->meta.merged_into = u;
unit_add_to_cleanup_queue(other);
return 0;
}
int unit_merge_by_name(Unit *u, const char *name) {
Unit *other;
assert(u);
assert(name);
if (!(other = manager_get_unit(u->meta.manager, name)))
return unit_add_name(u, name);
return unit_merge(u, other);
}
Unit* unit_follow_merge(Unit *u) {
assert(u);
while (u->meta.load_state == UNIT_MERGED)
assert_se(u = u->meta.merged_into);
return u;
}
int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
int r;
assert(u);
assert(c);
if (c->output != EXEC_OUTPUT_KERNEL && c->output != EXEC_OUTPUT_SYSLOG)
return 0;
/* If syslog or kernel logging is requested, make sure our own
* logging daemon is run first. */
if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET)) < 0)
return r;
if (u->meta.manager->running_as != MANAGER_SESSION)
if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET)) < 0)
return r;
return 0;
}
@ -389,22 +496,18 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s→ Unit %s:\n"
"%s\tDescription: %s\n"
"%s\tUnit Load State: %s\n"
"%s\tUnit Active State: %s\n"
"%s\tRecursive Stop: %s\n"
"%s\tStop When Unneeded: %s\n",
"%s\tUnit Active State: %s\n",
prefix, unit_id(u),
prefix, unit_description(u),
prefix, unit_load_state_to_string(u->meta.load_state),
prefix, unit_active_state_to_string(unit_active_state(u)),
prefix, yes_no(u->meta.recursive_stop),
prefix, yes_no(u->meta.stop_when_unneeded));
if (u->meta.fragment_path)
fprintf(f, "%s\tFragment Path: %s\n", prefix, u->meta.fragment_path);
prefix, unit_active_state_to_string(unit_active_state(u)));
SET_FOREACH(t, u->meta.names, i)
fprintf(f, "%s\tName: %s\n", prefix, t);
if (u->meta.fragment_path)
fprintf(f, "%s\tFragment Path: %s\n", prefix, u->meta.fragment_path);
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
Unit *other;
@ -415,12 +518,20 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), unit_id(other));
}
LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings)
fprintf(f, "%s\tControlGroup: %s:%s\n",
prefix, b->controller, b->path);
fprintf(f,
"%s\tRecursive Stop: %s\n"
"%s\tStop When Unneeded: %s\n",
prefix, yes_no(u->meta.recursive_stop),
prefix, yes_no(u->meta.stop_when_unneeded));
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
if (u->meta.load_state == UNIT_LOADED) {
LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings)
fprintf(f, "%s\tControlGroup: %s:%s\n",
prefix, b->controller, b->path);
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
}
if (u->meta.job)
job_dump(u->meta.job, f, prefix2);
@ -429,26 +540,55 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
}
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin(Unit *u) {
int r, ret;
int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state) {
int r;
assert(u);
assert(new_state);
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
/* Load a .socket file */
if ((r = unit_load_fragment(u)) < 0)
/* Load a .service file */
if ((r = unit_load_fragment(u, new_state)) < 0)
return r;
ret = r > 0;
if (*new_state == UNIT_STUB)
return -ENOENT;
/* Load drop-in directory data */
if ((r = unit_load_dropin(u)) < 0)
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
return r;
return ret;
return 0;
}
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state) {
int r;
assert(u);
assert(new_state);
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
/* 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, new_state)) < 0)
return r;
if (*new_state == UNIT_STUB)
*new_state = UNIT_LOADED;
/* Load drop-in directory data */
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
return r;
return 0;
}
int unit_load(Unit *u) {
int r;
UnitLoadState res;
assert(u);
@ -460,17 +600,30 @@ int unit_load(Unit *u) {
if (u->meta.load_state != UNIT_STUB)
return 0;
if (UNIT_VTABLE(u)->init)
if ((r = UNIT_VTABLE(u)->init(u)) < 0)
if (UNIT_VTABLE(u)->init) {
res = UNIT_STUB;
if ((r = UNIT_VTABLE(u)->init(u, &res)) < 0)
goto fail;
}
if (res == UNIT_STUB) {
r = -ENOENT;
goto fail;
}
u->meta.load_state = res;
assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into);
unit_add_to_dbus_queue(unit_follow_merge(u));
u->meta.load_state = UNIT_LOADED;
unit_add_to_dbus_queue(u);
return 0;
fail:
u->meta.load_state = UNIT_FAILED;
unit_add_to_dbus_queue(u);
log_error("Failed to load configuration for %s: %s", unit_id(u), strerror(-r));
return r;
}
@ -1227,7 +1380,8 @@ DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
[UNIT_STUB] = "stub",
[UNIT_LOADED] = "loaded",
[UNIT_FAILED] = "failed"
[UNIT_FAILED] = "failed",
[UNIT_MERGED] = "merged"
};
DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);

18
unit.h
View File

@ -60,6 +60,7 @@ enum UnitLoadState {
UNIT_STUB,
UNIT_LOADED,
UNIT_FAILED,
UNIT_MERGED,
_UNIT_LOAD_STATE_MAX,
_UNIT_LOAD_STATE_INVALID = -1
};
@ -116,8 +117,10 @@ enum UnitDependency {
struct Meta {
Manager *manager;
UnitType type;
UnitLoadState load_state;
Unit *merged_into;
char *id; /* One name is special because we use it for identification. Points to an entry in the names set */
@ -133,6 +136,7 @@ struct Meta {
bool in_load_queue:1;
bool in_dbus_queue:1;
bool in_cleanup_queue:1;
bool sent_dbus_new_signal:1;
/* If we go down, pull down everything that depends on us, too */
@ -155,6 +159,9 @@ struct Meta {
/* D-Bus queue */
LIST_FIELDS(Meta, dbus_queue);
/* Cleanup queue */
LIST_FIELDS(Meta, cleanup_queue);
};
#include "service.h"
@ -181,7 +188,7 @@ union Unit {
struct UnitVTable {
const char *suffix;
int (*init)(Unit *u);
int (*init)(Unit *u, UnitLoadState *new_state);
void (*done)(Unit *u);
int (*coldplug)(Unit *u);
@ -251,6 +258,8 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other);
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name);
int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name);
int unit_add_exec_dependencies(Unit *u, ExecContext *c);
int unit_add_cgroup(Unit *u, CGroupBonding *b);
int unit_add_cgroup_from_text(Unit *u, const char *name);
int unit_add_default_cgroup(Unit *u);
@ -261,10 +270,15 @@ int unit_set_description(Unit *u, const char *description);
void unit_add_to_load_queue(Unit *u);
void unit_add_to_dbus_queue(Unit *u);
void unit_add_to_cleanup_queue(Unit *u);
int unit_merge(Unit *u, Unit *other);
int unit_merge_by_name(Unit *u, const char *other);
int unit_load_fragment_and_dropin(Unit *u);
Unit *unit_follow_merge(Unit *u);
int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state);
int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state);
int unit_load(Unit *unit);
const char* unit_id(Unit *u);