From: Lennart Poettering Date: Tue, 26 Jan 2010 20:39:06 +0000 (+0100) Subject: s/name/unit X-Git-Tag: 0.git+20100605+dfd8ee-1~416 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=87f0e418cf2c58b3201d06a60e3696ec672d2662;p=systemd s/name/unit --- diff --git a/Makefile b/Makefile index c473262e..9a862207 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ CFLAGS=-Wall -Wextra -O0 -g -pipe -D_GNU_SOURCE -fdiagnostics-show-option -Wno-u LIBS=-lrt -lcap COMMON= \ - name.o \ + unit.o \ util.o \ set.o \ hashmap.o \ diff --git a/automount.c b/automount.c index b64bacbf..7b397c9b 100644 --- a/automount.c +++ b/automount.c @@ -2,43 +2,43 @@ #include -#include "name.h" +#include "unit.h" #include "automount.h" #include "load-fragment.h" #include "load-fstab.h" #include "load-dropin.h" -static int automount_init(Name *n) { +static int automount_init(Unit *u) { int r; - Automount *a = AUTOMOUNT(n); + Automount *a = AUTOMOUNT(u); assert(a); exec_context_init(&a->exec_context); /* Load a .automount file */ - if ((r = name_load_fragment(n)) < 0 && errno != -ENOENT) + if ((r = unit_load_fragment(u)) < 0 && errno != -ENOENT) return r; /* Load entry from /etc/fstab */ - if ((r = name_load_fstab(n)) < 0) + if ((r = unit_load_fstab(u)) < 0) return r; /* Load drop-in directory data */ - if ((r = name_load_dropin(n)) < 0) + if ((r = unit_load_dropin(u)) < 0) return r; return 0; } -static void automount_done(Name *n) { - Automount *d = AUTOMOUNT(n); +static void automount_done(Unit *u) { + Automount *d = AUTOMOUNT(u); assert(d); free(d->path); } -static void automount_dump(Name *n, FILE *f, const char *prefix) { +static void automount_dump(Unit *u, FILE *f, const char *prefix) { static const char* const state_table[_AUTOMOUNT_STATE_MAX] = { [AUTOMOUNT_DEAD] = "dead", @@ -59,7 +59,7 @@ static void automount_dump(Name *n, FILE *f, const char *prefix) { }; AutomountExecCommand c; - Automount *s = AUTOMOUNT(n); + Automount *s = AUTOMOUNT(u); assert(s); @@ -79,23 +79,23 @@ static void automount_dump(Name *n, FILE *f, const char *prefix) { } } -static NameActiveState automount_active_state(Name *n) { - - static const NameActiveState table[_AUTOMOUNT_STATE_MAX] = { - [AUTOMOUNT_DEAD] = NAME_INACTIVE, - [AUTOMOUNT_START_PRE] = NAME_ACTIVATING, - [AUTOMOUNT_START_POST] = NAME_ACTIVATING, - [AUTOMOUNT_WAITING] = NAME_ACTIVE, - [AUTOMOUNT_RUNNING] = NAME_ACTIVE, - [AUTOMOUNT_STOP_PRE] = NAME_DEACTIVATING, - [AUTOMOUNT_STOP_POST] = NAME_DEACTIVATING, - [AUTOMOUNT_MAINTAINANCE] = NAME_INACTIVE, +static UnitActiveState automount_active_state(Unit *u) { + + static const UnitActiveState table[_AUTOMOUNT_STATE_MAX] = { + [AUTOMOUNT_DEAD] = UNIT_INACTIVE, + [AUTOMOUNT_START_PRE] = UNIT_ACTIVATING, + [AUTOMOUNT_START_POST] = UNIT_ACTIVATING, + [AUTOMOUNT_WAITING] = UNIT_ACTIVE, + [AUTOMOUNT_RUNNING] = UNIT_ACTIVE, + [AUTOMOUNT_STOP_PRE] = UNIT_DEACTIVATING, + [AUTOMOUNT_STOP_POST] = UNIT_DEACTIVATING, + [AUTOMOUNT_MAINTAINANCE] = UNIT_INACTIVE, }; - return table[AUTOMOUNT(n)->state]; + return table[AUTOMOUNT(u)->state]; } -const NameVTable automount_vtable = { +const UnitVTable automount_vtable = { .suffix = ".mount", .init = automount_init, diff --git a/automount.h b/automount.h index b6dfd5bb..acd7ca2e 100644 --- a/automount.h +++ b/automount.h @@ -5,7 +5,7 @@ typedef struct Automount Automount; -#include "name.h" +#include "unit.h" typedef enum AutomountState { AUTOMOUNT_DEAD, @@ -41,6 +41,6 @@ struct Automount { Mount *mount; }; -extern const NameVTable automount_vtable; +extern const UnitVTable automount_vtable; #endif diff --git a/conf-parser.c b/conf-parser.c index c7f48eaf..78078cc0 100644 --- a/conf-parser.c +++ b/conf-parser.c @@ -109,7 +109,7 @@ static int parse_line(const char *filename, unsigned line, char **section, const } } - r = config_parse(fn, sections, t, userdata); + r = config_parse(fn, NULL, sections, t, userdata); free(path); return r; } @@ -162,19 +162,20 @@ static int parse_line(const char *filename, unsigned line, char **section, const } /* Go through the file and parse each line */ -int config_parse(const char *filename, const char* const * sections, const ConfigItem *t, void *userdata) { +int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) { unsigned line = 0; char *section = NULL; - FILE *f; int r; assert(filename); assert(t); - if (!(f = fopen(filename, "re"))) { - r = -errno; - log_error("Failed to open configuration file '%s': %s", filename, strerror(-r)); - goto finish; + if (!f) { + if (!(f = fopen(filename, "re"))) { + r = -errno; + log_error("Failed to open configuration file '%s': %s", filename, strerror(-r)); + goto finish; + } } while (!feof(f)) { diff --git a/conf-parser.h b/conf-parser.h index a8bed39d..eb9b2842 100644 --- a/conf-parser.h +++ b/conf-parser.h @@ -21,7 +21,7 @@ typedef struct ConfigItem { /* The configuration file parsing routine. Expects a table of * config_items in *t that is terminated by an item where lvalue is * NULL */ -int config_parse(const char *filename, const char* const * sections, const ConfigItem *t, void *userdata); +int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata); /* Generic parsers */ int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); diff --git a/device.c b/device.c index ec0a40a4..b8bab4b0 100644 --- a/device.c +++ b/device.c @@ -1,24 +1,24 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ -#include "name.h" +#include "unit.h" #include "device.h" #include "strv.h" -static void device_done(Name *n) { - Device *d = DEVICE(n); +static void device_done(Unit *u) { + Device *d = DEVICE(u); assert(d); strv_free(d->sysfs); } -static void device_dump(Name *n, FILE *f, const char *prefix) { +static void device_dump(Unit *u, FILE *f, const char *prefix) { static const char* const state_table[_DEVICE_STATE_MAX] = { [DEVICE_DEAD] = "dead", [DEVICE_AVAILABLE] = "available" }; - Device *s = DEVICE(n); + Device *s = DEVICE(u); assert(s); @@ -27,14 +27,14 @@ static void device_dump(Name *n, FILE *f, const char *prefix) { prefix, state_table[s->state]); } -static NameActiveState device_active_state(Name *n) { - return DEVICE(n)->state == DEVICE_DEAD ? NAME_INACTIVE : NAME_ACTIVE; +static UnitActiveState device_active_state(Unit *u) { + return DEVICE(u)->state == DEVICE_DEAD ? UNIT_INACTIVE : UNIT_ACTIVE; } -const NameVTable device_vtable = { +const UnitVTable device_vtable = { .suffix = ".device", - .init = name_load_fragment_and_dropin, + .init = unit_load_fragment_and_dropin, .done = device_done, .dump = device_dump, diff --git a/device.h b/device.h index e2597f71..09523ecf 100644 --- a/device.h +++ b/device.h @@ -5,7 +5,7 @@ typedef struct Device Device; -#include "name.h" +#include "unit.h" /* We simply watch devices, we cannot plug/unplug them. That * simplifies the state engine greatly */ @@ -24,6 +24,6 @@ struct Device { char **sysfs; }; -extern const NameVTable device_vtable; +extern const UnitVTable device_vtable; #endif diff --git a/fixme b/fixme index 7e5f9436..f3f723e3 100644 --- a/fixme +++ b/fixme @@ -3,9 +3,9 @@ - need gc for active jobs that nothing cares for -- need gc for names that are not referenced anymore +- need gc for units that are not referenced anymore -- refreshing of names (i.e. reload config files) +- refreshing of units (i.e. reload config files) - dbusification @@ -37,6 +37,6 @@ - templating/instances -- verify fragment data after loading: refuse cycles on yourself, service names contradicting, more than one Start executable, ... +- verify fragment data after loading: refuse cycles on yourself, service units contradicting, more than one Start executable, ... - rate limit startups diff --git a/job.c b/job.c index 4daed49f..ff30864e 100644 --- a/job.c +++ b/job.c @@ -6,12 +6,12 @@ #include "macro.h" #include "job.h" -Job* job_new(Manager *m, JobType type, Name *name) { +Job* job_new(Manager *m, JobType type, Unit *unit) { Job *j; assert(m); assert(type < _JOB_TYPE_MAX); - assert(name); + assert(unit); if (!(j = new0(Job, 1))) return NULL; @@ -19,7 +19,7 @@ Job* job_new(Manager *m, JobType type, Name *name) { j->manager = m; j->id = m->current_job_id++; j->type = type; - j->name = name; + j->unit = unit; /* We don't link it here, that's what job_dependency() is for */ @@ -31,8 +31,8 @@ void job_free(Job *j) { /* Detach from next 'bigger' objects */ if (j->installed) { - if (j->name->meta.job == j) - j->name->meta.job = NULL; + if (j->unit->meta.job == j) + j->unit->meta.job = NULL; hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id)); j->installed = false; @@ -142,7 +142,7 @@ void job_dump(Job *j, FILE*f, const char *prefix) { "%s\tState: %s\n" "%s\tForced: %s\n", prefix, j->id, - prefix, name_id(j->name), job_type_to_string(j->type), + prefix, unit_id(j->unit), job_type_to_string(j->type), prefix, job_state_table[j->state], prefix, yes_no(j->forced)); } @@ -257,12 +257,12 @@ bool job_type_is_conflicting(JobType a, JobType b) { bool job_is_runnable(Job *j) { Iterator i; - Name *other; + Unit *other; assert(j); assert(j->installed); - /* Checks whether there is any job running for the names this + /* Checks whether there is any job running for the units this * job needs to be running after (in the case of a 'positive' * job type) or before (in the case of a 'negative' job type * . */ @@ -277,7 +277,7 @@ bool job_is_runnable(Job *j) { * dependencies, regardless whether they are * starting or stopping something. */ - SET_FOREACH(other, j->name->meta.dependencies[NAME_AFTER], i) + SET_FOREACH(other, j->unit->meta.dependencies[UNIT_AFTER], i) if (other->meta.job) return false; } @@ -285,7 +285,7 @@ bool job_is_runnable(Job *j) { /* Also, if something else is being stopped and we should * change state after it, then lets wait. */ - SET_FOREACH(other, j->name->meta.dependencies[NAME_BEFORE], i) + SET_FOREACH(other, j->unit->meta.dependencies[UNIT_BEFORE], i) if (other->meta.job && (other->meta.job->type == JOB_STOP || other->meta.job->type == JOB_RESTART || @@ -328,16 +328,16 @@ int job_run_and_invalidate(Job *j) { switch (j->type) { case JOB_START: - r = name_start(j->name); + r = unit_start(j->unit); if (r == -EBADR) r = 0; break; case JOB_VERIFY_ACTIVE: { - NameActiveState t = name_active_state(j->name); - if (NAME_IS_ACTIVE_OR_RELOADING(t)) + UnitActiveState t = unit_active_state(j->unit); + if (UNIT_IS_ACTIVE_OR_RELOADING(t)) r = -EALREADY; - else if (t == NAME_ACTIVATING) + else if (t == UNIT_ACTIVATING) r = -EAGAIN; else r = -ENOEXEC; @@ -345,39 +345,39 @@ int job_run_and_invalidate(Job *j) { } case JOB_STOP: - r = name_stop(j->name); + r = unit_stop(j->unit); break; case JOB_RELOAD: - r = name_reload(j->name); + r = unit_reload(j->unit); break; case JOB_RELOAD_OR_START: - if (name_active_state(j->name) == NAME_ACTIVE) - r = name_reload(j->name); + if (unit_active_state(j->unit) == UNIT_ACTIVE) + r = unit_reload(j->unit); else - r = name_start(j->name); + r = unit_start(j->unit); break; case JOB_RESTART: { - NameActiveState t = name_active_state(j->name); - if (t == NAME_INACTIVE || t == NAME_ACTIVATING) { + UnitActiveState t = unit_active_state(j->unit); + if (t == UNIT_INACTIVE || t == UNIT_ACTIVATING) { j->type = JOB_START; - r = name_start(j->name); + r = unit_start(j->unit); } else - r = name_stop(j->name); + r = unit_stop(j->unit); break; } case JOB_TRY_RESTART: { - NameActiveState t = name_active_state(j->name); - if (t == NAME_INACTIVE || t == NAME_DEACTIVATING) + UnitActiveState t = unit_active_state(j->unit); + if (t == UNIT_INACTIVE || t == UNIT_DEACTIVATING) r = -ENOEXEC; - else if (t == NAME_ACTIVATING) { + else if (t == UNIT_ACTIVATING) { j->type = JOB_START; - r = name_start(j->name); + r = unit_start(j->unit); } else - r = name_stop(j->name); + r = unit_stop(j->unit); break; } @@ -397,9 +397,9 @@ int job_run_and_invalidate(Job *j) { } int job_finish_and_invalidate(Job *j, bool success) { - Name *n; - Name *other; - NameType t; + Unit *u; + Unit *other; + UnitType t; Iterator i; assert(j); @@ -413,7 +413,7 @@ int job_finish_and_invalidate(Job *j, bool success) { return 0; } - n = j->name; + u = j->unit; t = j->type; job_free(j); @@ -424,14 +424,14 @@ int job_finish_and_invalidate(Job *j, bool success) { t == JOB_VERIFY_ACTIVE || t == JOB_RELOAD_OR_START) { - SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRED_BY], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i) if (other->meta.job && (other->meta.type == JOB_START || other->meta.type == JOB_VERIFY_ACTIVE || other->meta.type == JOB_RELOAD_OR_START)) job_finish_and_invalidate(other->meta.job, false); - SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRED_BY], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRED_BY], i) if (other->meta.job && !other->meta.job->forced && (other->meta.type == JOB_START || @@ -441,7 +441,7 @@ int job_finish_and_invalidate(Job *j, bool success) { } else if (t == JOB_STOP) { - SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTS], i) if (other->meta.job && (t == JOB_START || t == JOB_VERIFY_ACTIVE || @@ -451,10 +451,10 @@ int job_finish_and_invalidate(Job *j, bool success) { } /* Try to start the next jobs that can be started */ - SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_AFTER], i) if (other->meta.job) job_schedule_run(other->meta.job); - SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_BEFORE], i) if (other->meta.job) job_schedule_run(other->meta.job); diff --git a/job.h b/job.h index 5527d6e4..dd5e31aa 100644 --- a/job.h +++ b/job.h @@ -13,12 +13,12 @@ typedef enum JobState JobState; typedef enum JobMode JobMode; #include "manager.h" -#include "name.h" +#include "unit.h" #include "hashmap.h" #include "list.h" enum JobType { - JOB_START, /* if a name does not support being started, we'll just wait until it becomes active */ + JOB_START, /* if a unit does not support being started, we'll just wait until it becomes active */ JOB_VERIFY_ACTIVE, JOB_STOP, @@ -64,7 +64,7 @@ struct Job { Manager *manager; uint32_t id; - Name *name; + Unit *unit; JobType type; JobState state; @@ -86,7 +86,7 @@ struct Job { }; -Job* job_new(Manager *m, JobType type, Name *name); +Job* job_new(Manager *m, JobType type, Unit *unit); void job_free(Job *job); void job_dump(Job *j, FILE*f, const char *prefix); diff --git a/load-dropin.c b/load-dropin.c index 7105cb86..3023cdae 100644 --- a/load-dropin.c +++ b/load-dropin.c @@ -2,8 +2,8 @@ #include "load-dropin.h" -int name_load_dropin(Name *n) { - assert(n); +int unit_load_dropin(Unit *u) { + assert(u); /* Load dependencies from supplementary drop-in directories */ diff --git a/load-dropin.h b/load-dropin.h index c4971a51..e1179acb 100644 --- a/load-dropin.h +++ b/load-dropin.h @@ -3,10 +3,10 @@ #ifndef fooloaddropinhfoo #define fooloaddropinhfoo -#include "name.h" +#include "unit.h" /* Read service data supplementary drop-in directories */ -int name_load_dropin(Name *n); +int unit_load_dropin(Unit *u); #endif diff --git a/load-fragment.c b/load-fragment.c index cf1e1498..7877a8c4 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -1,11 +1,13 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ +#include #include #include #include -#include +#include +#include -#include "name.h" +#include "unit.h" #include "strv.h" #include "conf-parser.h" #include "load-fragment.h" @@ -20,8 +22,8 @@ static int config_parse_deps( void *data, void *userdata) { - NameDependency d = PTR_TO_UINT(data); - Name *name = userdata; + UnitDependency d = PTR_TO_UINT(data); + Unit *u = userdata; char *w; size_t l; char *state; @@ -33,18 +35,18 @@ static int config_parse_deps( FOREACH_WORD(w, &l, rvalue, state) { char *t; int r; - Name *other; + Unit *other; if (!(t = strndup(w, l))) return -ENOMEM; - r = manager_load_name(name->meta.manager, t, &other); + r = manager_load_unit(u->meta.manager, t, &other); free(t); if (r < 0) return r; - if ((r = name_add_dependency(name, d, other)) < 0) + if ((r = unit_add_dependency(u, d, other)) < 0) return r; } @@ -60,8 +62,7 @@ static int config_parse_names( void *data, void *userdata) { - Set **set = data; - Name *name = userdata; + Unit *u = userdata; char *w; size_t l; char *state; @@ -74,42 +75,33 @@ static int config_parse_names( FOREACH_WORD(w, &l, rvalue, state) { char *t; int r; - Name *other; + Unit *other; if (!(t = strndup(w, l))) return -ENOMEM; - other = manager_get_name(name->meta.manager, t); + other = manager_get_unit(u->meta.manager, t); if (other) { - if (other != name) { + if (other != u) { - if (other->meta.load_state != NAME_STUB) { + if (other->meta.load_state != UNIT_STUB) { free(t); return -EEXIST; } - if ((r = name_merge(name, other)) < 0) { + if ((r = unit_merge(u, other)) < 0) { free(t); return r; } } } else { - - if (!*set) - if (!(*set = set_new(trivial_hash_func, trivial_compare_func))) { - free(t); - return -ENOMEM; - } - - if ((r = set_put(*set, t)) < 0) { + if ((r = unit_add_name(u, t)) < 0) { free(t); return r; } - - t = NULL; } free(t); @@ -458,17 +450,127 @@ static int config_parse_service_restart( return 0; } -int name_load_fragment(Name *n) { - - static const char* const section_table[_NAME_TYPE_MAX] = { - [NAME_SERVICE] = "Service", - [NAME_TIMER] = "Timer", - [NAME_SOCKET] = "Socket", - [NAME_TARGET] = "Target", - [NAME_DEVICE] = "Device", - [NAME_MOUNT] = "Mount", - [NAME_AUTOMOUNT] = "Automount", - [NAME_SNAPSHOT] = "Snapshot" +#define FOLLOW_MAX 8 + +static char *build_path(const char *path, const char *filename) { + char *e, *r; + size_t k; + + assert(path); + assert(filename); + + /* This removes the last component of path and appends + * filename, unless the latter is absolute anyway or the + * former isn't */ + + if (filename[0] == '/') + return strdup(filename); + + if (!(e = strrchr(path, '/'))) + return strdup(filename); + + k = strlen(filename); + if (!(r = new(char, e-path+1+k+1))) + return NULL; + + memcpy(r, path, e-path+1); + memcpy(r+(e-path)+1, filename, k+1); + + return r; +} + +static int open_follow(const char **filename, FILE **_f, Set *names) { + unsigned c; + int fd, r; + FILE *f; + char *n = NULL; + const char *fn; + + assert(filename); + assert(*filename); + assert(_f); + assert(names); + + fn = *filename; + + for (c = 0; c < FOLLOW_MAX; c++) { + char *target, *k, *name; + + /* Add the file name we are currently looking at to + * the names of this unit */ + name = file_name_from_path(fn); + if (!set_get(names, name)) { + + if (!(name = strdup(name))) { + r = -ENOMEM; + goto finish; + } + + if ((r = set_put(names, name)) < 0) { + free(name); + goto finish; + } + + free(name); + } + + /* Try to open the file name, but don' if its a symlink */ + fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd >= 0 || errno != ELOOP) + break; + + /* Hmm, so this is a symlink. Let's read the name, and follow it manually */ + if ((r = readlink_malloc(fn, &target)) < 0) + goto finish; + + k = build_path(fn, target); + free(target); + + if (!k) { + r = -ENOMEM; + goto finish; + } + + free(n); + fn = n = k; + } + + if (c >= FOLLOW_MAX) { + r = -ELOOP; + goto finish; + } + + if (fd < 0) { + r = -errno; + goto finish; + } + + if (!(f = fdopen(fd, "r"))) { + r = -errno; + assert(close_nointr(fd) == 0); + goto finish; + } + + *_f = f; + *filename = fn; + r = 0; + +finish: + free(n); + return r; +} + +int unit_load_fragment(Unit *u) { + + static const char* const section_table[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = "Service", + [UNIT_TIMER] = "Timer", + [UNIT_SOCKET] = "Socket", + [UNIT_TARGET] = "Target", + [UNIT_DEVICE] = "Device", + [UNIT_MOUNT] = "Mount", + [UNIT_AUTOMOUNT] = "Automount", + [UNIT_SNAPSHOT] = "Snapshot" }; #define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \ @@ -482,73 +584,110 @@ int name_load_fragment(Name *n) { { "Environment", config_parse_strv, &(context).environment, section } const ConfigItem items[] = { - { "Names", config_parse_names, &n->meta.names, "Meta" }, - { "Description", config_parse_string, &n->meta.description, "Meta" }, - { "Requires", config_parse_deps, UINT_TO_PTR(NAME_REQUIRES), "Meta" }, - { "SoftRequires", config_parse_deps, UINT_TO_PTR(NAME_SOFT_REQUIRES), "Meta" }, - { "Wants", config_parse_deps, UINT_TO_PTR(NAME_WANTS), "Meta" }, - { "Requisite", config_parse_deps, UINT_TO_PTR(NAME_REQUISITE), "Meta" }, - { "SoftRequisite", config_parse_deps, UINT_TO_PTR(NAME_SOFT_REQUISITE), "Meta" }, - { "Conflicts", config_parse_deps, UINT_TO_PTR(NAME_CONFLICTS), "Meta" }, - { "Before", config_parse_deps, UINT_TO_PTR(NAME_BEFORE), "Meta" }, - { "After", config_parse_deps, UINT_TO_PTR(NAME_AFTER), "Meta" }, - - { "PIDFile", config_parse_path, &n->service.pid_file, "Service" }, - { "ExecStartPre", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_START_PRE], "Service" }, - { "ExecStart", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_START], "Service" }, - { "ExecStartPost", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_START_POST], "Service" }, - { "ExecReload", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_RELOAD], "Service" }, - { "ExecStop", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_STOP], "Service" }, - { "ExecStopPost", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_STOP_POST], "Service" }, - { "RestartSec", config_parse_usec, &n->service.restart_usec, "Service" }, - { "TimeoutSec", config_parse_usec, &n->service.timeout_usec, "Service" }, - { "Type", config_parse_service_type, &n->service, "Service" }, - { "Restart", config_parse_service_restart, &n->service, "Service" }, - EXEC_CONTEXT_CONFIG_ITEMS(n->service.exec_context, "Service"), - - { "ListenStream", config_parse_listen, &n->socket, "Socket" }, - { "ListenDatagram", config_parse_listen, &n->socket, "Socket" }, - { "ListenSequentialPacket", config_parse_listen, &n->socket, "Socket" }, - { "ListenFIFO", config_parse_listen, &n->socket, "Socket" }, - { "BindIPv6Only", config_parse_socket_bind, &n->socket, "Socket" }, - { "Backlog", config_parse_unsigned, &n->socket.backlog, "Socket" }, - { "ExecStartPre", config_parse_exec, &n->service.exec_command[SOCKET_EXEC_START_PRE], "Socket" }, - { "ExecStartPost", config_parse_exec, &n->service.exec_command[SOCKET_EXEC_START_POST], "Socket" }, - { "ExecStopPre", config_parse_exec, &n->service.exec_command[SOCKET_EXEC_STOP_PRE], "Socket" }, - { "ExecStopPost", config_parse_exec, &n->service.exec_command[SOCKET_EXEC_STOP_POST], "Socket" }, - EXEC_CONTEXT_CONFIG_ITEMS(n->socket.exec_context, "Socket"), - - EXEC_CONTEXT_CONFIG_ITEMS(n->automount.exec_context, "Automount"), + { "Names", config_parse_names, u, "Meta" }, + { "Description", config_parse_string, &u->meta.description, "Meta" }, + { "Requires", config_parse_deps, UINT_TO_PTR(UNIT_REQUIRES), "Meta" }, + { "SoftRequires", config_parse_deps, UINT_TO_PTR(UNIT_SOFT_REQUIRES), "Meta" }, + { "Wants", config_parse_deps, UINT_TO_PTR(UNIT_WANTS), "Meta" }, + { "Requisite", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE), "Meta" }, + { "SoftRequisite", config_parse_deps, UINT_TO_PTR(UNIT_SOFT_REQUISITE), "Meta" }, + { "Conflicts", config_parse_deps, UINT_TO_PTR(UNIT_CONFLICTS), "Meta" }, + { "Before", config_parse_deps, UINT_TO_PTR(UNIT_BEFORE), "Meta" }, + { "After", config_parse_deps, UINT_TO_PTR(UNIT_AFTER), "Meta" }, + + { "PIDFile", config_parse_path, &u->service.pid_file, "Service" }, + { "ExecStartPre", config_parse_exec, &u->service.exec_command[SERVICE_EXEC_START_PRE], "Service" }, + { "ExecStart", config_parse_exec, &u->service.exec_command[SERVICE_EXEC_START], "Service" }, + { "ExecStartPost", config_parse_exec, &u->service.exec_command[SERVICE_EXEC_START_POST], "Service" }, + { "ExecReload", config_parse_exec, &u->service.exec_command[SERVICE_EXEC_RELOAD], "Service" }, + { "ExecStop", config_parse_exec, &u->service.exec_command[SERVICE_EXEC_STOP], "Service" }, + { "ExecStopPost", config_parse_exec, &u->service.exec_command[SERVICE_EXEC_STOP_POST], "Service" }, + { "RestartSec", config_parse_usec, &u->service.restart_usec, "Service" }, + { "TimeoutSec", config_parse_usec, &u->service.timeout_usec, "Service" }, + { "Type", config_parse_service_type, &u->service, "Service" }, + { "Restart", config_parse_service_restart, &u->service, "Service" }, + EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"), + + { "ListenStream", config_parse_listen, &u->socket, "Socket" }, + { "ListenDatagram", config_parse_listen, &u->socket, "Socket" }, + { "ListenSequentialPacket", config_parse_listen, &u->socket, "Socket" }, + { "ListenFIFO", config_parse_listen, &u->socket, "Socket" }, + { "BindIPv6Only", config_parse_socket_bind, &u->socket, "Socket" }, + { "Backlog", config_parse_unsigned, &u->socket.backlog, "Socket" }, + { "ExecStartPre", config_parse_exec, &u->service.exec_command[SOCKET_EXEC_START_PRE], "Socket" }, + { "ExecStartPost", config_parse_exec, &u->service.exec_command[SOCKET_EXEC_START_POST], "Socket" }, + { "ExecStopPre", config_parse_exec, &u->service.exec_command[SOCKET_EXEC_STOP_PRE], "Socket" }, + { "ExecStopPost", config_parse_exec, &u->service.exec_command[SOCKET_EXEC_STOP_POST], "Socket" }, + EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"), + + EXEC_CONTEXT_CONFIG_ITEMS(u->automount.exec_context, "Automount"), { NULL, NULL, NULL, NULL } }; #undef EXEC_CONTEXT_CONFIG_ITEMS - char *t; + char *t, *k; int r; const char *sections[3]; Iterator i; + Set *symlink_names; - assert(n); - assert(n->meta.load_state == NAME_STUB); + assert(u); + assert(u->meta.load_state == UNIT_STUB); sections[0] = "Meta"; - sections[1] = section_table[n->meta.type]; + sections[1] = section_table[u->meta.type]; sections[2] = NULL; - SET_FOREACH(t, n->meta.names, i) { + if (!(symlink_names = set_new(string_hash_func, string_compare_func))) + return -ENOMEM; - /* Try to find a name we can load this with */ - if ((r = config_parse(t, sections, items, n)) == -ENOENT) - continue; + /* Try to find a name we can load this with */ + SET_FOREACH(t, u->meta.names, i) { + FILE *f; + char *fn; - /* Yay, we succeeded! Now let's call this our identifier */ - if (r == 0) - n->meta.id = t; + /* Clear the symlink name set first */ + while ((k = set_steal_first(symlink_names))) + free(k); - return r; + /* Instead of opening the path right away, we manually + * follow all symlinks and add their name to our unit + * name set while doing so */ + fn = t; + if ((r = open_follow((const char**) &fn, &f, symlink_names)) < 0) { + if (r == -ENOENT) + continue; + + goto finish; + } + + /* Now, parse the file contents */ + r = config_parse(fn, f, sections, items, u); + if (fn != t) + free(fn); + if (r < 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; + + /* Yay, we succeeded! Now let's call this our identifier */ + u->meta.id = t; + goto finish; } - return -ENOENT; + + r = -ENOENT; + +finish: + while ((k = set_steal_first(symlink_names))) + free(k); + + set_free(symlink_names); + + return r; } diff --git a/load-fragment.h b/load-fragment.h index 523426fa..475bf64a 100644 --- a/load-fragment.h +++ b/load-fragment.h @@ -3,10 +3,10 @@ #ifndef fooloadfragmenthfoo #define fooloadfragmenthfoo -#include "name.h" +#include "unit.h" /* Read service data from .desktop file style configuration fragments */ -int name_load_fragment(Name *n); +int unit_load_fragment(Unit *u); #endif diff --git a/load-fstab.c b/load-fstab.c index cfefcb66..d611426c 100644 --- a/load-fstab.c +++ b/load-fstab.c @@ -2,8 +2,8 @@ #include "load-fstab.h" -int name_load_fstab(Name *n) { - assert(n); +int unit_load_fstab(Unit *u) { + assert(u); /* Load dependencies from /etc/fstab */ diff --git a/load-fstab.h b/load-fstab.h index e7056502..b0cef7dd 100644 --- a/load-fstab.h +++ b/load-fstab.h @@ -3,10 +3,10 @@ #ifndef fooloadfstabhfoo #define fooloadfstabhfoo -#include "name.h" +#include "unit.h" /* Read service data from /etc/fstab */ -int name_load_fstab(Name *n); +int unit_load_fstab(Unit *u); #endif diff --git a/main.c b/main.c index 99f7d683..e139c4b1 100644 --- a/main.c +++ b/main.c @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { Manager *m = NULL; - Name *target = NULL; + Unit *target = NULL; Job *job = NULL; int r, retval = 1; @@ -21,7 +21,7 @@ int main(int argc, char *argv[]) { goto finish; } - if ((r = manager_load_name(m, "default.target", &target)) < 0) { + if ((r = manager_load_unit(m, "default.target", &target)) < 0) { log_error("Failed to load default target: %s", strerror(-r)); goto finish; } @@ -31,8 +31,8 @@ int main(int argc, char *argv[]) { goto finish; } - printf("→ By names:\n"); - manager_dump_names(m, stdout, "\t"); + printf("→ By units:\n"); + manager_dump_units(m, stdout, "\t"); printf("→ By jobs:\n"); manager_dump_jobs(m, stdout, "\t"); diff --git a/manager.c b/manager.c index 7950a8a7..819164ca 100644 --- a/manager.c +++ b/manager.c @@ -26,7 +26,7 @@ Manager* manager_new(void) { m->signal_fd = m->epoll_fd = -1; - if (!(m->names = hashmap_new(string_hash_func, string_compare_func))) + if (!(m->units = hashmap_new(string_hash_func, string_compare_func))) goto fail; if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func))) @@ -63,18 +63,18 @@ fail: } void manager_free(Manager *m) { - Name *n; + Unit *u; Job *j; assert(m); - while ((n = hashmap_first(m->names))) - name_free(n); - - while ((j = hashmap_steal_first(m->transaction_jobs))) + while ((j = hashmap_first(m->transaction_jobs))) job_free(j); - hashmap_free(m->names); + while ((u = hashmap_first(m->units))) + unit_free(u); + + hashmap_free(m->units); hashmap_free(m->jobs); hashmap_free(m->transaction_jobs); hashmap_free(m->watch_pids); @@ -99,13 +99,13 @@ static void transaction_delete_job(Manager *m, Job *j) { job_free(j); } -static void transaction_delete_name(Manager *m, Name *n) { +static void transaction_delete_unit(Manager *m, Unit *u) { Job *j; - /* Deletes all jobs associated with a certain name from the + /* Deletes all jobs associated with a certain unit from the * transaction */ - while ((j = hashmap_get(m->transaction_jobs, n))) + while ((j = hashmap_get(m->transaction_jobs, u))) transaction_delete_job(m, j); } @@ -129,7 +129,7 @@ static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsi assert(m); - /* A recursive sweep through the graph that marks all names + /* A recursive sweep through the graph that marks all units * that matter to the anchor job, i.e. are directly or * indirectly a dependency of the anchor job via paths that * are fully marked as mattering. */ @@ -145,7 +145,7 @@ static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsi if (!l->matters) continue; - /* This name has already been marked */ + /* This unit has already been marked */ if (l->object->generation == generation) continue; @@ -161,7 +161,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job assert(j); assert(other); - assert(j->name == other->name); + assert(j->unit == other->unit); assert(!j->installed); /* Merges 'other' into 'j' and then deletes j. */ @@ -240,7 +240,7 @@ 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", name_id(d->name), job_type_to_string(d->type)); + 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); return 0; } @@ -291,8 +291,8 @@ static int transaction_merge_jobs(Manager *m) { assert_se(job_type_merge(&t, k->type) == 0); /* If an active job is mergeable, merge it too */ - if (j->name->meta.job) - job_type_merge(&t, j->name->meta.job->type); /* Might fail. Which is OK */ + if (j->unit->meta.job) + job_type_merge(&t, j->unit->meta.job->type); /* Might fail. Which is OK */ while ((k = j->transaction_next)) { if (j->installed) { @@ -309,11 +309,11 @@ static int transaction_merge_jobs(Manager *m) { return 0; } -static bool name_matters_to_anchor(Name *n, Job *j) { - assert(n); +static bool unit_matters_to_anchor(Unit *u, Job *j) { + assert(u); assert(!j->transaction_prev); - /* Checks whether at least one of the jobs for this name + /* Checks whether at least one of the jobs for this unit * matters to the anchor. */ LIST_FOREACH(transaction, j, j) @@ -325,7 +325,7 @@ static bool name_matters_to_anchor(Name *n, Job *j) { static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation) { Iterator i; - Name *n; + Unit *u; int r; assert(m); @@ -349,11 +349,11 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) { if (!k->installed && - !name_matters_to_anchor(k->name, k)) { + !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", name_id(k->name), job_type_to_string(k->type)); - transaction_delete_name(m, k->name); + log_debug("Breaking order cycle by deleting job %s/%s", unit_id(k->unit), job_type_to_string(k->type)); + transaction_delete_unit(m, k->unit); return -EAGAIN; } @@ -372,17 +372,17 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned j->generation = generation; /* We assume that the the dependencies are bidirectional, and - * hence can ignore NAME_AFTER */ - SET_FOREACH(n, j->name->meta.dependencies[NAME_BEFORE], i) { + * hence can ignore UNIT_AFTER */ + SET_FOREACH(u, j->unit->meta.dependencies[UNIT_BEFORE], i) { Job *o; - /* Is there a job for this name? */ - if (!(o = hashmap_get(m->transaction_jobs, n))) + /* Is there a job for this unit? */ + if (!(o = hashmap_get(m->transaction_jobs, u))) /* Ok, there is no job for this in the * transaction, but maybe there is already one * running? */ - if (!(o = n->meta.job)) + if (!(o = u->meta.job)) continue; if ((r = transaction_verify_order_one(m, o, j, generation)) < 0) @@ -427,7 +427,7 @@ static void transaction_collect_garbage(Manager *m) { if (j->object_list) continue; - log_debug("Garbage collecting job %s/%s", name_id(j->name), job_type_to_string(j->type)); + log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type)); transaction_delete_job(m, j); again = true; break; @@ -451,9 +451,9 @@ static int transaction_is_destructive(Manager *m, JobMode mode) { assert(!j->transaction_prev); assert(!j->transaction_next); - if (j->name->meta.job && - j->name->meta.job != j && - !job_type_is_superset(j->type, j->name->meta.job->type)) + if (j->unit->meta.job && + j->unit->meta.job != j && + !job_type_is_superset(j->type, j->unit->meta.job->type)) return -EEXIST; } @@ -483,12 +483,12 @@ static void transaction_minimize_impact(Manager *m) { /* Would this stop a running service? * Would this change an existing job? * If so, let's drop this entry */ - if ((j->type != JOB_STOP || NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(j->name))) && - (!j->name->meta.job || job_type_is_conflicting(j->type, j->name->meta.job->state))) + if ((j->type != JOB_STOP || UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) && + (!j->unit->meta.job || job_type_is_conflicting(j->type, j->unit->meta.job->state))) continue; /* Ok, let's get rid of this */ - log_debug("Deleting %s/%s to minimize impact", name_id(j->name), job_type_to_string(j->type)); + log_debug("Deleting %s/%s to minimize impact", unit_id(j->unit), job_type_to_string(j->type)); transaction_delete_job(m, j); again = true; break; @@ -524,10 +524,10 @@ static int transaction_apply(Manager *m, JobMode mode) { if (j->installed) continue; - if (j->name->meta.job) - job_free(j->name->meta.job); + if (j->unit->meta.job) + job_free(j->unit->meta.job); - j->name->meta.job = j; + j->unit->meta.job = j; j->installed = true; /* We're fully installed. Now let's free data we don't @@ -629,21 +629,21 @@ rollback: return r; } -static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool force, bool *is_new) { +static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool force, bool *is_new) { Job *j, *f; int r; assert(m); - assert(name); + assert(unit); /* Looks for an axisting prospective job and returns that. If * it doesn't exist it is created and added to the prospective * jobs list. */ - f = hashmap_get(m->transaction_jobs, name); + f = hashmap_get(m->transaction_jobs, unit); LIST_FOREACH(transaction, j, f) { - assert(j->name == name); + assert(j->unit == unit); if (j->type == type) { if (is_new) @@ -652,9 +652,9 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool f } } - if (name->meta.job && name->meta.job->type == type) - j = name->meta.job; - else if (!(j = job_new(m, type, name))) + if (unit->meta.job && unit->meta.job->type == type) + j = unit->meta.job; + else if (!(j = job_new(m, type, unit))) return NULL; j->generation = 0; @@ -664,7 +664,7 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool f LIST_PREPEND(Job, transaction, f, j); - if ((r = hashmap_replace(m->transaction_jobs, name, f)) < 0) { + if ((r = hashmap_replace(m->transaction_jobs, unit, f)) < 0) { job_free(j); return NULL; } @@ -682,9 +682,9 @@ void manager_transaction_unlink_job(Manager *m, Job *j) { if (j->transaction_prev) j->transaction_prev->transaction_next = j->transaction_next; else if (j->transaction_next) - hashmap_replace(m->transaction_jobs, j->name, j->transaction_next); + hashmap_replace(m->transaction_jobs, j->unit, j->transaction_next); else - hashmap_remove_value(m->transaction_jobs, j->name, j); + hashmap_remove_value(m->transaction_jobs, j->unit, j); if (j->transaction_next) j->transaction_next->transaction_prev = j->transaction_prev; @@ -701,32 +701,32 @@ void manager_transaction_unlink_job(Manager *m, Job *j) { if (other) { log_debug("Deleting job %s/%s as dependency of job %s/%s", - name_id(other->name), job_type_to_string(other->type), - name_id(j->name), job_type_to_string(j->type)); + 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); } } } -static int transaction_add_job_and_dependencies(Manager *m, JobType type, Name *name, 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 force, Job **_ret) { Job *ret; Iterator i; - Name *dep; + Unit *dep; int r; bool is_new; assert(m); assert(type < _JOB_TYPE_MAX); - assert(name); + assert(unit); - if (name->meta.load_state != NAME_LOADED) + if (unit->meta.load_state != UNIT_LOADED) return -EINVAL; - if (!name_job_is_applicable(name, type)) + if (!unit_job_is_applicable(unit, type)) return -EBADR; /* First add the job. */ - if (!(ret = transaction_add_one_job(m, type, name, force, &is_new))) + if (!(ret = transaction_add_one_job(m, type, unit, force, &is_new))) return -ENOMEM; /* Then, add a link to the job. */ @@ -736,28 +736,28 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Name * if (is_new) { /* Finally, recursively add in all dependencies. */ if (type == JOB_START || type == JOB_RELOAD_OR_START) { - SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], i) + 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->name->meta.dependencies[NAME_SOFT_REQUIRES], i) + 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) goto fail; - SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], i) + 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 && r != -EBADR) goto fail; - SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], i) + 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->name->meta.dependencies[NAME_SOFT_REQUISITE], i) + 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) goto fail; - SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], i) + 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) goto fail; } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) { - SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], i) + 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) goto fail; } @@ -771,16 +771,16 @@ fail: return r; } -int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, bool force, Job **_ret) { +int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret) { int r; Job *ret; assert(m); assert(type < _JOB_TYPE_MAX); - assert(name); + assert(unit); assert(mode < _JOB_MODE_MAX); - if ((r = transaction_add_job_and_dependencies(m, type, name, NULL, true, force, &ret))) { + if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret))) { transaction_abort(m); return r; } @@ -800,11 +800,11 @@ Job *manager_get_job(Manager *m, uint32_t id) { return hashmap_get(m->jobs, UINT32_TO_PTR(id)); } -Name *manager_get_name(Manager *m, const char *name) { +Unit *manager_get_unit(Manager *m, const char *name) { assert(m); assert(name); - return hashmap_get(m->names, name); + return hashmap_get(m->units, name); } static void dispatch_load_queue(Manager *m) { @@ -818,20 +818,20 @@ static void dispatch_load_queue(Manager *m) { m->dispatching_load_queue = true; - /* Dispatches the load queue. Takes a name from the queue and + /* Dispatches the load queue. Takes a unit from the queue and * tries to load its data until the queue is empty */ while ((meta = m->load_queue)) { assert(meta->in_load_queue); - name_load(NAME(meta)); + unit_load(UNIT(meta)); } m->dispatching_load_queue = false; } -int manager_load_name(Manager *m, const char *name, Name **_ret) { - Name *ret; +int manager_load_unit(Manager *m, const char *name, Unit **_ret) { + Unit *ret; int r; assert(m); @@ -841,20 +841,20 @@ int manager_load_name(Manager *m, const char *name, Name **_ret) { /* This will load the service information files, but not actually * start any services or anything */ - if ((ret = manager_get_name(m, name))) { + if ((ret = manager_get_unit(m, name))) { *_ret = ret; return 0; } - if (!(ret = name_new(m))) + if (!(ret = unit_new(m))) return -ENOMEM; - if ((r = name_add_name(ret, name)) < 0) { - name_free(ret); + if ((r = unit_add_name(ret, name)) < 0) { + unit_free(ret); return r; } - name_add_to_load_queue(ret); + unit_add_to_load_queue(ret); dispatch_load_queue(m); *_ret = ret; @@ -872,17 +872,17 @@ void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) { job_dump(j, f, prefix); } -void manager_dump_names(Manager *s, FILE *f, const char *prefix) { +void manager_dump_units(Manager *s, FILE *f, const char *prefix) { Iterator i; - Name *n; + Unit *u; const char *t; assert(s); assert(f); - HASHMAP_FOREACH_KEY(n, t, s->names, i) - if (name_id(n) == t) - name_dump(n, f, prefix); + HASHMAP_FOREACH_KEY(u, t, s->units, i) + if (unit_id(u) == t) + unit_dump(u, f, prefix); } void manager_clear_jobs(Manager *m) { @@ -919,7 +919,7 @@ static int manager_dispatch_sigchld(Manager *m) { for (;;) { siginfo_t si; - Name *n; + Unit *u; zero(si); if (waitid(P_ALL, 0, &si, WNOHANG) < 0) @@ -931,10 +931,10 @@ static int manager_dispatch_sigchld(Manager *m) { if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED) continue; - if (!(n = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) + if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) continue; - NAME_VTABLE(n)->sigchld_event(n, si.si_pid, si.si_code, si.si_status); + UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status); } return 0; @@ -990,21 +990,21 @@ static int process_event(Manager *m, struct epoll_event *ev) { break; case MANAGER_FD: { - Name *n; + Unit *u; - /* Some fd event, to be dispatched to the names */ - assert_se(n = ev->data.ptr); - NAME_VTABLE(n)->fd_event(n, ev->data.fd, ev->events); + /* Some fd event, to be dispatched to the units */ + assert_se(u = ev->data.ptr); + UNIT_VTABLE(u)->fd_event(u, ev->data.fd, ev->events); break; } case MANAGER_TIMER: { - Name *n; - uint64_t u; + Unit *u; + uint64_t v; ssize_t k; - /* Some timer event, to be dispatched to the names */ - if ((k = read(ev->data.fd, &u, sizeof(u))) != sizeof(u)) { + /* Some timer event, to be dispatched to the units */ + if ((k = read(ev->data.fd, &v, sizeof(v))) != sizeof(v)) { if (k < 0 && (errno == EINTR || errno == EAGAIN)) break; @@ -1012,8 +1012,8 @@ static int process_event(Manager *m, struct epoll_event *ev) { return k < 0 ? -errno : -EIO; } - assert_se(n = ev->data.ptr); - NAME_VTABLE(n)->timer_event(n, ev->data.fd, u); + assert_se(u = ev->data.ptr); + UNIT_VTABLE(u)->timer_event(u, ev->data.fd, v); break; } diff --git a/manager.h b/manager.h index ddc9610e..432d730d 100644 --- a/manager.h +++ b/manager.h @@ -10,7 +10,7 @@ typedef struct Manager Manager; typedef enum ManagerEventType ManagerEventType; -#include "name.h" +#include "unit.h" #include "job.h" #include "hashmap.h" #include "list.h" @@ -25,28 +25,28 @@ enum ManagerEventType { struct Manager { uint32_t current_job_id; - /* Note that the set of names we know of is allowed to be + /* Note that the set of units we know of is allowed to be * incosistent. However the subset of it that is loaded may * not, and the list of jobs may neither. */ - /* Active jobs and names */ - Hashmap *names; /* name string => Name object n:1 */ + /* Active jobs and units */ + Hashmap *units; /* name string => Unit object n:1 */ Hashmap *jobs; /* job id => Job object 1:1 */ - /* Names that need to be loaded */ + /* Units that need to be loaded */ LIST_HEAD(Meta, load_queue); /* this is actually more a stack than a queue, but uh. */ /* Jobs that need to be run */ LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */ /* Jobs to be added */ - Hashmap *transaction_jobs; /* Name object => Job object list 1:1 */ + Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */ JobDependency *transaction_anchor; bool dispatching_load_queue:1; bool dispatching_run_queue:1; - Hashmap *watch_pids; /* pid => Name object n:1 */ + Hashmap *watch_pids; /* pid => Unit object n:1 */ int epoll_fd; int signal_fd; @@ -56,12 +56,12 @@ Manager* manager_new(void); void manager_free(Manager *m); Job *manager_get_job(Manager *m, uint32_t id); -Name *manager_get_name(Manager *m, const char *name); +Unit *manager_get_unit(Manager *m, const char *name); -int manager_load_name(Manager *m, const char *name, Name **_ret); -int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, bool force, Job **_ret); +int manager_load_unit(Manager *m, const char *name, Unit **_ret); +int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret); -void manager_dump_names(Manager *s, FILE *f, const char *prefix); +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); diff --git a/mount.c b/mount.c index 21d34778..f4a6d6f2 100644 --- a/mount.c +++ b/mount.c @@ -2,41 +2,41 @@ #include -#include "name.h" +#include "unit.h" #include "mount.h" #include "load-fragment.h" #include "load-fstab.h" #include "load-dropin.h" -static int mount_init(Name *n) { +static int mount_init(Unit *u) { int r; - Mount *m = MOUNT(n); + Mount *m = MOUNT(u); assert(m); /* Load a .mount file */ - if ((r = name_load_fragment(n)) < 0 && errno != -ENOENT) + if ((r = unit_load_fragment(u)) < 0 && errno != -ENOENT) return r; /* Load entry from /etc/fstab */ - if ((r = name_load_fstab(n)) < 0) + if ((r = unit_load_fstab(u)) < 0) return r; /* Load drop-in directory data */ - if ((r = name_load_dropin(n)) < 0) + if ((r = unit_load_dropin(u)) < 0) return r; return r; } -static void mount_done(Name *n) { - Mount *d = MOUNT(n); +static void mount_done(Unit *u) { + Mount *d = MOUNT(u); assert(d); free(d->path); } -static void mount_dump(Name *n, FILE *f, const char *prefix) { +static void mount_dump(Unit *u, FILE *f, const char *prefix) { static const char* const state_table[_MOUNT_STATE_MAX] = { [MOUNT_DEAD] = "dead", @@ -46,7 +46,7 @@ static void mount_dump(Name *n, FILE *f, const char *prefix) { [MOUNT_MAINTAINANCE] = "maintainance" }; - Mount *s = MOUNT(n); + Mount *s = MOUNT(u); assert(s); @@ -57,20 +57,20 @@ static void mount_dump(Name *n, FILE *f, const char *prefix) { prefix, s->path); } -static NameActiveState mount_active_state(Name *n) { +static UnitActiveState mount_active_state(Unit *u) { - static const NameActiveState table[_MOUNT_STATE_MAX] = { - [MOUNT_DEAD] = NAME_INACTIVE, - [MOUNT_MOUNTING] = NAME_ACTIVATING, - [MOUNT_MOUNTED] = NAME_ACTIVE, - [MOUNT_UNMOUNTING] = NAME_DEACTIVATING, - [MOUNT_MAINTAINANCE] = NAME_INACTIVE, + static const UnitActiveState table[_MOUNT_STATE_MAX] = { + [MOUNT_DEAD] = UNIT_INACTIVE, + [MOUNT_MOUNTING] = UNIT_ACTIVATING, + [MOUNT_MOUNTED] = UNIT_ACTIVE, + [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING, + [MOUNT_MAINTAINANCE] = UNIT_INACTIVE, }; - return table[MOUNT(n)->state]; + return table[MOUNT(u)->state]; } -const NameVTable mount_vtable = { +const UnitVTable mount_vtable = { .suffix = ".mount", .init = mount_init, diff --git a/mount.h b/mount.h index b437b2e7..734e96cb 100644 --- a/mount.h +++ b/mount.h @@ -5,7 +5,7 @@ typedef struct Mount Mount; -#include "name.h" +#include "unit.h" typedef enum MountState { MOUNT_DEAD, @@ -23,6 +23,6 @@ struct Mount { char *path; }; -extern const NameVTable mount_vtable; +extern const UnitVTable mount_vtable; #endif diff --git a/name.c b/name.c deleted file mode 100644 index e755eda3..00000000 --- a/name.c +++ /dev/null @@ -1,829 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ - -#include -#include -#include -#include -#include -#include - -#include "set.h" -#include "name.h" -#include "macro.h" -#include "strv.h" -#include "load-fragment.h" -#include "load-dropin.h" -#include "log.h" - -const NameVTable * const name_vtable[_NAME_TYPE_MAX] = { - [NAME_SERVICE] = &service_vtable, - [NAME_TIMER] = &timer_vtable, - [NAME_SOCKET] = &socket_vtable, - [NAME_TARGET] = &target_vtable, - [NAME_DEVICE] = &device_vtable, - [NAME_MOUNT] = &mount_vtable, - [NAME_AUTOMOUNT] = &automount_vtable, - [NAME_SNAPSHOT] = &snapshot_vtable -}; - -NameType name_type_from_string(const char *n) { - NameType t; - - assert(n); - - for (t = 0; t < _NAME_TYPE_MAX; t++) - if (endswith(n, name_vtable[t]->suffix)) - return t; - - return _NAME_TYPE_INVALID; -} - -#define VALID_CHARS \ - "0123456789" \ - "abcdefghijklmnopqrstuvwxyz" \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ - "-_" - -bool name_is_valid(const char *n) { - NameType t; - const char *e, *i; - - assert(n); - - if (strlen(n) >= NAME_MAX) - return false; - - t = name_type_from_string(n); - if (t < 0 || t >= _NAME_TYPE_MAX) - return false; - - if (!(e = strrchr(n, '.'))) - return false; - - for (i = n; i < e; i++) - if (!strchr(VALID_CHARS, *i)) - return false; - - return true; -} - -Name *name_new(Manager *m) { - Name *n; - - assert(m); - - if (!(n = new0(Name, 1))) - return NULL; - - if (!(n->meta.names = set_new(string_hash_func, string_compare_func))) { - free(n); - return NULL; - } - - n->meta.manager = m; - n->meta.type = _NAME_TYPE_INVALID; - - return n; -} - -int name_add_name(Name *n, const char *text) { - NameType t; - char *s; - int r; - - assert(n); - assert(text); - - if ((t = name_type_from_string(text)) == _NAME_TYPE_INVALID) - return -EINVAL; - - if (n->meta.type != _NAME_TYPE_INVALID && t != n->meta.type) - return -EINVAL; - - if (!(s = strdup(text))) - return -ENOMEM; - - if ((r = set_put(n->meta.names, s)) < 0) { - free(s); - return r; - } - - if ((r = hashmap_put(n->meta.manager->names, s, n)) < 0) { - set_remove(n->meta.names, s); - free(s); - return r; - } - - n->meta.type = t; - - if (!n->meta.id) - n->meta.id = s; - - return 0; -} - -void name_add_to_load_queue(Name *n) { - assert(n); - - if (n->meta.load_state != NAME_STUB || n->meta.in_load_queue) - return; - - LIST_PREPEND(Meta, load_queue, n->meta.manager->load_queue, &n->meta); - n->meta.in_load_queue = true; -} - -static void bidi_set_free(Name *name, Set *s) { - Iterator i; - Name *other; - - assert(name); - - /* Frees the set and makes sure we are dropped from the - * inverse pointers */ - - SET_FOREACH(other, s, i) { - NameDependency d; - - for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) - set_remove(other->meta.dependencies[d], name); - } - - set_free(s); -} - -void name_free(Name *name) { - NameDependency d; - Iterator i; - char *t; - - assert(name); - - /* Detach from next 'bigger' objects */ - - SET_FOREACH(t, name->meta.names, i) - hashmap_remove_value(name->meta.manager->names, t, name); - - if (name->meta.in_load_queue) - LIST_REMOVE(Meta, load_queue, name->meta.manager->load_queue, &name->meta); - - if (name->meta.load_state == NAME_LOADED) - if (NAME_VTABLE(name)->done) - NAME_VTABLE(name)->done(name); - - /* Free data and next 'smaller' objects */ - if (name->meta.job) - job_free(name->meta.job); - - for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) - bidi_set_free(name, name->meta.dependencies[d]); - - free(name->meta.description); - - while ((t = set_steal_first(name->meta.names))) - free(t); - set_free(name->meta.names); - - free(name); -} - -NameActiveState name_active_state(Name *name) { - assert(name); - - if (name->meta.load_state != NAME_LOADED) - return NAME_INACTIVE; - - return NAME_VTABLE(name)->active_state(name); -} - -static int ensure_merge(Set **s, Set *other) { - - if (!other) - return 0; - - if (*s) - return set_merge(*s, other); - - if (!(*s = set_copy(other))) - return -ENOMEM; - - return 0; -} - -/* FIXME: Does not rollback on failure! */ -int name_merge(Name *name, Name *other) { - int r; - NameDependency d; - - assert(name); - assert(other); - assert(name->meta.manager == other->meta.manager); - - /* This merges 'other' into 'name'. FIXME: This does not - * rollback on failure. */ - - if (name->meta.type != other->meta.type) - return -EINVAL; - - if (other->meta.load_state != NAME_STUB) - return -EINVAL; - - /* Merge names */ - if ((r = ensure_merge(&name->meta.names, other->meta.names)) < 0) - return r; - - /* Merge dependencies */ - for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) - /* fixme, the inverse mapping is missing */ - if ((r = ensure_merge(&name->meta.dependencies[d], other->meta.dependencies[d])) < 0) - return r; - - return 0; -} - -const char* name_id(Name *n) { - assert(n); - - if (n->meta.id) - return n->meta.id; - - return set_first(n->meta.names); -} - -const char *name_description(Name *n) { - assert(n); - - if (n->meta.description) - return n->meta.description; - - return name_id(n); -} - -void name_dump(Name *n, FILE *f, const char *prefix) { - - static const char* const load_state_table[_NAME_LOAD_STATE_MAX] = { - [NAME_STUB] = "stub", - [NAME_LOADED] = "loaded", - [NAME_FAILED] = "failed" - }; - - static const char* const active_state_table[_NAME_ACTIVE_STATE_MAX] = { - [NAME_ACTIVE] = "active", - [NAME_INACTIVE] = "inactive", - [NAME_ACTIVATING] = "activating", - [NAME_DEACTIVATING] = "deactivating" - }; - - static const char* const dependency_table[_NAME_DEPENDENCY_MAX] = { - [NAME_REQUIRES] = "Requires", - [NAME_SOFT_REQUIRES] = "SoftRequires", - [NAME_WANTS] = "Wants", - [NAME_REQUISITE] = "Requisite", - [NAME_SOFT_REQUISITE] = "SoftRequisite", - [NAME_REQUIRED_BY] = "RequiredBy", - [NAME_SOFT_REQUIRED_BY] = "SoftRequiredBy", - [NAME_WANTED_BY] = "WantedBy", - [NAME_CONFLICTS] = "Conflicts", - [NAME_BEFORE] = "Before", - [NAME_AFTER] = "After", - }; - - char *t; - NameDependency d; - Iterator i; - char *prefix2; - - assert(n); - - if (!prefix) - prefix = ""; - prefix2 = strappend(prefix, "\t"); - if (!prefix2) - prefix2 = ""; - - fprintf(f, - "%s→ Name %s:\n" - "%s\tDescription: %s\n" - "%s\tName Load State: %s\n" - "%s\tName Active State: %s\n", - prefix, name_id(n), - prefix, name_description(n), - prefix, load_state_table[n->meta.load_state], - prefix, active_state_table[name_active_state(n)]); - - SET_FOREACH(t, n->meta.names, i) - fprintf(f, "%s\tName: %s\n", prefix, t); - - for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) { - Name *other; - - if (set_isempty(n->meta.dependencies[d])) - continue; - - SET_FOREACH(other, n->meta.dependencies[d], i) - fprintf(f, "%s\t%s: %s\n", prefix, dependency_table[d], name_id(other)); - } - - if (NAME_VTABLE(n)->dump) - NAME_VTABLE(n)->dump(n, f, prefix2); - - if (n->meta.job) - job_dump(n->meta.job, f, prefix2); - - free(prefix2); -} - -static int verify_type(Name *name) { - char *n; - Iterator i; - - assert(name); - - /* Checks that all aliases of this name have the same and valid type */ - - SET_FOREACH(n, name->meta.names, i) { - NameType t; - - if ((t = name_type_from_string(n)) == _NAME_TYPE_INVALID) - return -EINVAL; - - if (name->meta.type == _NAME_TYPE_INVALID) { - name->meta.type = t; - continue; - } - - if (name->meta.type != t) - return -EINVAL; - } - - if (name->meta.type == _NAME_TYPE_INVALID) - return -EINVAL; - - return 0; -} - -/* Common implementation for multiple backends */ -int name_load_fragment_and_dropin(Name *n) { - int r; - - assert(n); - - /* Load a .socket file */ - if ((r = name_load_fragment(n)) < 0) - return r; - - /* Load drop-in directory data */ - if ((r = name_load_dropin(n)) < 0) - return r; - - return 0; -} - -int name_load(Name *name) { - int r; - - assert(name); - - if (name->meta.in_load_queue) { - LIST_REMOVE(Meta, load_queue, name->meta.manager->load_queue, &name->meta); - name->meta.in_load_queue = false; - } - - if (name->meta.load_state != NAME_STUB) - return 0; - - if ((r = verify_type(name)) < 0) - return r; - - if (NAME_VTABLE(name)->init) - if ((r = NAME_VTABLE(name)->init(name)) < 0) - goto fail; - - name->meta.load_state = NAME_LOADED; - return 0; - -fail: - name->meta.load_state = NAME_FAILED; - return r; -} - -/* Errors: - * -EBADR: This name type does not support starting. - * -EALREADY: Name is already started. - * -EAGAIN: An operation is already in progress. Retry later. - */ -int name_start(Name *n) { - NameActiveState state; - - assert(n); - - if (!NAME_VTABLE(n)->start) - return -EBADR; - - state = name_active_state(n); - if (NAME_IS_ACTIVE_OR_RELOADING(state)) - return -EALREADY; - - /* 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 name is in some "auto - * restart" state where it waits for a holdoff timer to elapse - * before it will start again. */ - - return NAME_VTABLE(n)->start(n); -} - -bool name_can_start(Name *n) { - assert(n); - - return !!NAME_VTABLE(n)->start; -} - -/* Errors: - * -EBADR: This name type does not support stopping. - * -EALREADY: Name is already stopped. - * -EAGAIN: An operation is already in progress. Retry later. - */ -int name_stop(Name *n) { - NameActiveState state; - - assert(n); - - if (!NAME_VTABLE(n)->stop) - return -EBADR; - - state = name_active_state(n); - if (state == NAME_INACTIVE) - return -EALREADY; - - if (state == NAME_DEACTIVATING) - return 0; - - return NAME_VTABLE(n)->stop(n); -} - -/* Errors: - * -EBADR: This name type does not support reloading. - * -ENOEXEC: Name is not started. - * -EAGAIN: An operation is already in progress. Retry later. - */ -int name_reload(Name *n) { - NameActiveState state; - - assert(n); - - if (!name_can_reload(n)) - return -EBADR; - - state = name_active_state(n); - if (name_active_state(n) == NAME_ACTIVE_RELOADING) - return -EALREADY; - - if (name_active_state(n) != NAME_ACTIVE) - return -ENOEXEC; - - return NAME_VTABLE(n)->reload(n); -} - -bool name_can_reload(Name *n) { - assert(n); - - if (!NAME_VTABLE(n)->reload) - return false; - - if (!NAME_VTABLE(n)->can_reload) - return true; - - return NAME_VTABLE(n)->can_reload(n); -} - -static void retroactively_start_dependencies(Name *n) { - Iterator i; - Name *other; - - assert(n); - assert(NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(n))); - - SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], i) - if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) - manager_add_job(n->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); - - SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], i) - if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) - manager_add_job(n->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); - - SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], i) - if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) - manager_add_job(n->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); - - SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], i) - if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) - manager_add_job(n->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); - - SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], i) - if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) - manager_add_job(n->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); -} - -static void retroactively_stop_dependencies(Name *n) { - Iterator i; - Name *other; - - assert(n); - assert(NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(n))); - - SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRED_BY], i) - if (!NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(other))) - manager_add_job(n->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); -} - -void name_notify(Name *n, NameActiveState os, NameActiveState ns) { - assert(n); - assert(os < _NAME_ACTIVE_STATE_MAX); - assert(ns < _NAME_ACTIVE_STATE_MAX); - assert(!(os == NAME_ACTIVE && ns == NAME_ACTIVATING)); - assert(!(os == NAME_INACTIVE && ns == NAME_DEACTIVATING)); - - if (os == ns) - return; - - if (!NAME_IS_ACTIVE_OR_RELOADING(os) && NAME_IS_ACTIVE_OR_RELOADING(ns)) - n->meta.active_enter_timestamp = now(CLOCK_REALTIME); - else if (NAME_IS_ACTIVE_OR_RELOADING(os) && !NAME_IS_ACTIVE_OR_RELOADING(ns)) - n->meta.active_exit_timestamp = now(CLOCK_REALTIME); - - if (n->meta.job) { - - if (n->meta.job->state == JOB_WAITING) - - /* 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_schedule_run(n->meta.job); - - else { - assert(n->meta.job->state == JOB_RUNNING); - - /* Let's check of this state change - * constitutes a finished job, or maybe - * cotradicts a running job and hence needs to - * invalidate jobs. */ - - switch (n->meta.job->type) { - - case JOB_START: - case JOB_VERIFY_ACTIVE: - - if (NAME_IS_ACTIVE_OR_RELOADING(ns)) { - job_finish_and_invalidate(n->meta.job, true); - return; - } else if (ns == NAME_ACTIVATING) - return; - else - job_finish_and_invalidate(n->meta.job, false); - - break; - - case JOB_RELOAD: - case JOB_RELOAD_OR_START: - - if (ns == NAME_ACTIVE) { - job_finish_and_invalidate(n->meta.job, true); - return; - } else if (ns == NAME_ACTIVATING || ns == NAME_ACTIVE_RELOADING) - return; - else - job_finish_and_invalidate(n->meta.job, false); - - break; - - case JOB_STOP: - case JOB_RESTART: - case JOB_TRY_RESTART: - - if (ns == NAME_INACTIVE) { - job_finish_and_invalidate(n->meta.job, true); - return; - } else if (ns == NAME_DEACTIVATING) - return; - else - job_finish_and_invalidate(n->meta.job, false); - - break; - - default: - assert_not_reached("Job type unknown"); - } - } - } - - /* If this state change happened without being requested by a - * job, then let's retroactively start or stop dependencies */ - - if (NAME_IS_INACTIVE_OR_DEACTIVATING(os) && NAME_IS_ACTIVE_OR_ACTIVATING(ns)) - retroactively_start_dependencies(n); - else if (NAME_IS_ACTIVE_OR_ACTIVATING(os) && NAME_IS_INACTIVE_OR_DEACTIVATING(ns)) - retroactively_stop_dependencies(n); -} - -int name_watch_fd(Name *n, int fd, uint32_t events) { - struct epoll_event ev; - - assert(n); - assert(fd >= 0); - - zero(ev); - ev.data.fd = fd; - ev.data.ptr = n; - ev.data.u32 = MANAGER_FD; - ev.events = events; - - if (epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) >= 0) - return 0; - - if (errno == EEXIST) - if (epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_MOD, fd, &ev) >= 0) - return 0; - - return -errno; -} - -void name_unwatch_fd(Name *n, int fd) { - assert(n); - assert(fd >= 0); - - assert_se(epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_DEL, fd, NULL) >= 0 || errno == ENOENT); -} - -int name_watch_pid(Name *n, pid_t pid) { - assert(n); - assert(pid >= 1); - - return hashmap_put(n->meta.manager->watch_pids, UINT32_TO_PTR(pid), n); -} - -void name_unwatch_pid(Name *n, pid_t pid) { - assert(n); - assert(pid >= 1); - - hashmap_remove(n->meta.manager->watch_pids, UINT32_TO_PTR(pid)); -} - -int name_watch_timer(Name *n, usec_t delay, int *id) { - struct epoll_event ev; - int fd; - struct itimerspec its; - int flags; - bool ours; - - assert(n); - assert(id); - - /* This will try to reuse the old timer if there is one */ - - if (*id >= 0) { - ours = false; - fd = *id; - - } else { - ours = true; - - if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) - return -errno; - } - - 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; - - zero(ev); - ev.data.fd = fd; - ev.data.ptr = n; - ev.data.u32 = MANAGER_TIMER; - ev.events = POLLIN; - - if (epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) - goto fail; - - *id = fd; - return 0; - -fail: - if (ours) - assert_se(close_nointr(fd) == 0); - - return -errno; -} - -void name_unwatch_timer(Name *n, int *id) { - assert(n); - assert(id); - - if (*id >= 0) { - assert_se(epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_DEL, *id, NULL) >= 0); - assert_se(close_nointr(*id) == 0); - - *id = -1; - } -} - -char *name_change_suffix(const char *t, const char *suffix) { - char *e, *n; - size_t a, b; - - assert(t); - assert(name_is_valid(t)); - assert(suffix); - - assert_se(e = strrchr(t, '.')); - a = e - t; - b = strlen(suffix); - - if (!(n = new(char, a + b + 1))) - return NULL; - - memcpy(n, t, a); - memcpy(n+a, suffix, b+1); - - return n; -} - -bool name_job_is_applicable(Name *n, JobType j) { - assert(n); - assert(j >= 0 && j < _JOB_TYPE_MAX); - - switch (j) { - case JOB_VERIFY_ACTIVE: - case JOB_START: - return true; - - case JOB_STOP: - case JOB_RESTART: - case JOB_TRY_RESTART: - return name_can_start(n); - - case JOB_RELOAD: - return name_can_reload(n); - - case JOB_RELOAD_OR_START: - return name_can_reload(n) && name_can_start(n); - - default: - assert_not_reached("Invalid job type"); - } -} - -int name_add_dependency(Name *n, NameDependency d, Name *other) { - - static const NameDependency inverse_table[_NAME_DEPENDENCY_MAX] = { - [NAME_REQUIRES] = NAME_REQUIRED_BY, - [NAME_SOFT_REQUIRES] = NAME_SOFT_REQUIRED_BY, - [NAME_WANTS] = NAME_WANTED_BY, - [NAME_REQUISITE] = NAME_REQUIRED_BY, - [NAME_SOFT_REQUISITE] = NAME_SOFT_REQUIRED_BY, - [NAME_REQUIRED_BY] = _NAME_DEPENDENCY_INVALID, - [NAME_SOFT_REQUIRED_BY] = _NAME_DEPENDENCY_INVALID, - [NAME_WANTED_BY] = _NAME_DEPENDENCY_INVALID, - [NAME_CONFLICTS] = NAME_CONFLICTS, - [NAME_BEFORE] = NAME_AFTER, - [NAME_AFTER] = NAME_BEFORE - }; - int r; - - assert(n); - assert(d >= 0 && d < _NAME_DEPENDENCY_MAX); - assert(inverse_table[d] != _NAME_DEPENDENCY_INVALID); - assert(other); - - if (n == other) - return 0; - - if ((r = set_ensure_allocated(&n->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) - return r; - - if ((r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) - return r; - - if ((r = set_put(n->meta.dependencies[d], other)) < 0) - return r; - - if ((r = set_put(other->meta.dependencies[inverse_table[d]], n)) < 0) { - set_remove(n->meta.dependencies[d], other); - return r; - } - - return 0; -} diff --git a/name.h b/name.h deleted file mode 100644 index cf1861f9..00000000 --- a/name.h +++ /dev/null @@ -1,236 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ - -#ifndef foonamehfoo -#define foonamehfoo - -#include -#include - -typedef union Name Name; -typedef struct Meta Meta; -typedef struct NameVTable NameVTable; -typedef enum NameType NameType; -typedef enum NameLoadState NameLoadState; -typedef enum NameActiveState NameActiveState; -typedef enum NameDependency NameDependency; - -#include "job.h" -#include "manager.h" -#include "set.h" -#include "util.h" -#include "list.h" -#include "socket-util.h" -#include "execute.h" -#include "util.h" - -#define NAME_MAX 32 -#define DEFAULT_TIMEOUT_USEC (20*USEC_PER_SEC) -#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) - -enum NameType { - NAME_SERVICE = 0, - NAME_TIMER, - NAME_SOCKET, - NAME_TARGET, - NAME_DEVICE, - NAME_MOUNT, - NAME_AUTOMOUNT, - NAME_SNAPSHOT, - _NAME_TYPE_MAX, - _NAME_TYPE_INVALID = -1, -}; - -enum NameLoadState { - NAME_STUB, - NAME_LOADED, - NAME_FAILED, - _NAME_LOAD_STATE_MAX -}; - -enum NameActiveState { - NAME_ACTIVE, - NAME_ACTIVE_RELOADING, - NAME_INACTIVE, - NAME_ACTIVATING, - NAME_DEACTIVATING, - _NAME_ACTIVE_STATE_MAX -}; - -static inline bool NAME_IS_ACTIVE_OR_RELOADING(NameActiveState t) { - return t == NAME_ACTIVE || t == NAME_ACTIVE_RELOADING; -} - -static inline bool NAME_IS_ACTIVE_OR_ACTIVATING(NameActiveState t) { - return t == NAME_ACTIVE || t == NAME_ACTIVATING || t == NAME_ACTIVE_RELOADING; -} - -static inline bool NAME_IS_INACTIVE_OR_DEACTIVATING(NameActiveState t) { - return t == NAME_INACTIVE || t == NAME_DEACTIVATING; -} - -enum NameDependency { - /* Positive dependencies */ - NAME_REQUIRES, - NAME_SOFT_REQUIRES, - NAME_WANTS, - NAME_REQUISITE, - NAME_SOFT_REQUISITE, - - /* Inverse of the above */ - NAME_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */ - NAME_SOFT_REQUIRED_BY, /* inverse of 'soft_requires' and 'soft_requisite' is 'soft_required_by' */ - NAME_WANTED_BY, /* inverse of 'wants' */ - - /* Negative dependencies */ - NAME_CONFLICTS, /* inverse of 'conflicts' is 'conflicts' */ - - /* Order */ - NAME_BEFORE, /* inverse of before is after and vice versa */ - NAME_AFTER, - - _NAME_DEPENDENCY_MAX, - _NAME_DEPENDENCY_INVALID = -1 -}; - -struct Meta { - Manager *manager; - NameType type; - NameLoadState load_state; - - char *id; /* One name is special because we use it for identification. Points to an entry in the names set */ - - Set *names; - Set *dependencies[_NAME_DEPENDENCY_MAX]; - - char *description; - - /* If there is something to do with this name, then this is - * the job for it */ - Job *job; - - bool in_load_queue:1; - - usec_t active_enter_timestamp; - usec_t active_exit_timestamp; - - /* Load queue */ - LIST_FIELDS(Meta, load_queue); -}; - -#include "service.h" -#include "timer.h" -#include "socket.h" -#include "target.h" -#include "device.h" -#include "mount.h" -#include "automount.h" -#include "snapshot.h" - -union Name { - Meta meta; - Service service; - Timer timer; - Socket socket; - Target target; - Device device; - Mount mount; - Automount automount; - Snapshot snapshot; -}; - -struct NameVTable { - const char *suffix; - - int (*init)(Name *n); - void (*done)(Name *n); - - void (*dump)(Name *n, FILE *f, const char *prefix); - - int (*start)(Name *n); - int (*stop)(Name *n); - int (*reload)(Name *n); - - bool (*can_reload)(Name *n); - - /* Boils down the more complex internal state of this name to - * a simpler one that the engine can understand */ - NameActiveState (*active_state)(Name *n); - - void (*fd_event)(Name *n, int fd, uint32_t events); - void (*sigchld_event)(Name *n, pid_t pid, int code, int status); - void (*timer_event)(Name *n, int id, uint64_t n_elapsed); - - void (*retry)(Name *n); -}; - -extern const NameVTable * const name_vtable[_NAME_TYPE_MAX]; - -#define NAME_VTABLE(n) name_vtable[(n)->meta.type] - -/* For casting a name into the various name types */ -#define DEFINE_CAST(UPPERCASE, MixedCase) \ - static inline MixedCase* UPPERCASE(Name *name) { \ - if (!name || name->meta.type != NAME_##UPPERCASE) \ - return NULL; \ - \ - return (MixedCase*) name; \ - } - -/* For casting the various name types into a name */ -#define NAME(o) ((Name*) (o)) - -DEFINE_CAST(SOCKET, Socket); -DEFINE_CAST(TIMER, Timer); -DEFINE_CAST(SERVICE, Service); -DEFINE_CAST(TARGET, Target); -DEFINE_CAST(DEVICE, Device); -DEFINE_CAST(MOUNT, Mount); -DEFINE_CAST(AUTOMOUNT, Automount); -DEFINE_CAST(SNAPSHOT, Snapshot); - -NameType name_type_from_string(const char *n); -bool name_is_valid(const char *n); - -Name *name_new(Manager *m); -void name_free(Name *name); - -int name_add_name(Name *n, const char *text); -int name_add_dependency(Name *n, NameDependency d, Name *other); - -void name_add_to_load_queue(Name *n); - -int name_merge(Name *name, Name *other); - -int name_load_fragment_and_dropin(Name *n); -int name_load(Name *name); - -const char* name_id(Name *n); -const char *name_description(Name *n); - -NameActiveState name_active_state(Name *name); - -void name_dump(Name *n, FILE *f, const char *prefix); - -bool name_can_reload(Name *n); -bool name_can_start(Name *n); - -int name_start(Name *n); -int name_stop(Name *n); -int name_reload(Name *n); - -void name_notify(Name *n, NameActiveState os, NameActiveState ns); - -int name_watch_fd(Name *n, int fd, uint32_t events); -void name_unwatch_fd(Name *n, int fd); - -int name_watch_pid(Name *n, pid_t pid); -void name_unwatch_pid(Name *n, pid_t pid); - -int name_watch_timer(Name *n, usec_t delay, int *id); -void name_unwatch_timer(Name *n, int *id); - -char *name_change_suffix(const char *t, const char *suffix); - -bool name_job_is_applicable(Name *n, JobType j); - -#endif diff --git a/service.c b/service.c index 1e4824d4..ef10cdbf 100644 --- a/service.c +++ b/service.c @@ -3,31 +3,31 @@ #include #include -#include "name.h" +#include "unit.h" #include "service.h" #include "load-fragment.h" #include "load-dropin.h" #include "log.h" -static const NameActiveState state_table[_SERVICE_STATE_MAX] = { - [SERVICE_DEAD] = NAME_INACTIVE, - [SERVICE_START_PRE] = NAME_ACTIVATING, - [SERVICE_START] = NAME_ACTIVATING, - [SERVICE_START_POST] = NAME_ACTIVATING, - [SERVICE_RUNNING] = NAME_ACTIVE, - [SERVICE_RELOAD] = NAME_ACTIVE_RELOADING, - [SERVICE_STOP] = NAME_DEACTIVATING, - [SERVICE_STOP_SIGTERM] = NAME_DEACTIVATING, - [SERVICE_STOP_SIGKILL] = NAME_DEACTIVATING, - [SERVICE_STOP_POST] = NAME_DEACTIVATING, - [SERVICE_FINAL_SIGTERM] = NAME_DEACTIVATING, - [SERVICE_FINAL_SIGKILL] = NAME_DEACTIVATING, - [SERVICE_MAINTAINANCE] = NAME_INACTIVE, - [SERVICE_AUTO_RESTART] = NAME_ACTIVATING, +static const UnitActiveState state_table[_SERVICE_STATE_MAX] = { + [SERVICE_DEAD] = UNIT_INACTIVE, + [SERVICE_START_PRE] = UNIT_ACTIVATING, + [SERVICE_START] = UNIT_ACTIVATING, + [SERVICE_START_POST] = UNIT_ACTIVATING, + [SERVICE_RUNNING] = UNIT_ACTIVE, + [SERVICE_RELOAD] = UNIT_ACTIVE_RELOADING, + [SERVICE_STOP] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, + [SERVICE_STOP_POST] = UNIT_DEACTIVATING, + [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, + [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, + [SERVICE_MAINTAINANCE] = UNIT_INACTIVE, + [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING, }; -static void service_done(Name *n) { - Service *s = SERVICE(n); +static void service_done(Unit *u) { + Service *s = SERVICE(u); assert(s); @@ -41,16 +41,16 @@ static void service_done(Name *n) { /* This will leak a process, but at least no memory or any of * our resources */ if (s->main_pid > 0) { - name_unwatch_pid(n, s->main_pid); + unit_unwatch_pid(u, s->main_pid); s->main_pid = 0; } if (s->control_pid > 0) { - name_unwatch_pid(n, s->control_pid); + unit_unwatch_pid(u, s->control_pid); s->control_pid = 0; } - name_unwatch_timer(n, &s->timer_id); + unit_unwatch_timer(u, &s->timer_id); } static int service_load_sysv(Service *s) { @@ -62,9 +62,9 @@ static int service_load_sysv(Service *s) { return -ENOENT; } -static int service_init(Name *n) { +static int service_init(Unit *u) { int r; - Service *s = SERVICE(n); + Service *s = SERVICE(u); assert(s); @@ -84,27 +84,27 @@ static int service_init(Name *n) { s->state = SERVICE_DEAD; /* Load a .service file */ - r = name_load_fragment(n); + r = unit_load_fragment(u); /* Load a classic init script as a fallback */ if (r == -ENOENT) r = service_load_sysv(s); if (r < 0) { - service_done(n); + service_done(u); return r; } /* Load dropin directory data */ - if ((r = name_load_dropin(n)) < 0) { - service_done(n); + if ((r = unit_load_dropin(u)) < 0) { + service_done(u); return r; } return 0; } -static void service_dump(Name *n, FILE *f, const char *prefix) { +static void service_dump(Unit *u, FILE *f, const char *prefix) { static const char* const state_table[_SERVICE_STATE_MAX] = { [SERVICE_DEAD] = "dead", @@ -133,7 +133,7 @@ static void service_dump(Name *n, FILE *f, const char *prefix) { }; ServiceExecCommand c; - Service *s = SERVICE(n); + Service *s = SERVICE(u); char *prefix2; assert(s); @@ -216,7 +216,7 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_FINAL_SIGTERM && state != SERVICE_FINAL_SIGKILL && state != SERVICE_AUTO_RESTART) - name_unwatch_timer(NAME(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_id); if (state != SERVICE_START_POST && state != SERVICE_RUNNING && @@ -225,7 +225,7 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_STOP_SIGTERM && state != SERVICE_STOP_SIGKILL) if (s->main_pid >= 0) { - name_unwatch_pid(NAME(s), s->main_pid); + unit_unwatch_pid(UNIT(s), s->main_pid); s->main_pid = 0; } @@ -240,7 +240,7 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_FINAL_SIGTERM && state != SERVICE_FINAL_SIGKILL) if (s->control_pid >= 0) { - name_unwatch_pid(NAME(s), s->control_pid); + unit_unwatch_pid(UNIT(s), s->control_pid); s->control_pid = 0; } @@ -252,7 +252,7 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_STOP_POST) s->control_command = NULL; - name_notify(NAME(s), state_table[old_state], state_table[s->state]); + unit_notify(UNIT(s), state_table[old_state], state_table[s->state]); } static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { @@ -266,21 +266,21 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { assert(fds); assert(n_fds); - SET_FOREACH(t, NAME(s)->meta.names, i) { + SET_FOREACH(t, UNIT(s)->meta.names, i) { char *k; - Name *p; + Unit *p; int *cfds; unsigned cn_fds; /* Look for all socket objects that go by any of our - * names and collect their fds */ + * units and collect their fds */ - if (!(k = name_change_suffix(t, ".socket"))) { + if (!(k = unit_name_change_suffix(t, ".socket"))) { r = -ENOMEM; goto fail; } - p = manager_get_name(NAME(s)->meta.manager, k); + p = manager_get_unit(UNIT(s)->meta.manager, k); free(k); if ((r = socket_collect_fds(SOCKET(p), &cfds, &cn_fds)) < 0) @@ -335,15 +335,15 @@ static int service_spawn(Service *s, ExecCommand *c, bool timeout, bool pass_fds goto fail; if (timeout) { - if ((r = name_watch_timer(NAME(s), s->timeout_usec, &s->timer_id)) < 0) + if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_id)) < 0) goto fail; } else - name_unwatch_timer(NAME(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_id); if ((r = exec_spawn(c, &s->exec_context, fds, n_fds, &pid)) < 0) goto fail; - if ((r = name_watch_pid(NAME(s), pid)) < 0) + if ((r = unit_watch_pid(UNIT(s), pid)) < 0) /* FIXME: we need to do something here */ goto fail; @@ -356,7 +356,7 @@ fail: free(fds); if (timeout) - name_unwatch_timer(NAME(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_id); return r; } @@ -372,7 +372,7 @@ static void service_enter_dead(Service *s, bool success, bool allow_restart) { (s->restart == SERVICE_RESTART_ALWAYS || (s->restart == SERVICE_RESTART_ON_SUCCESS && !s->failure))) { - if ((r = name_watch_timer(NAME(s), s->restart_usec, &s->timer_id)) < 0) + if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_id)) < 0) goto fail; service_set_state(s, SERVICE_AUTO_RESTART); @@ -382,7 +382,7 @@ static void service_enter_dead(Service *s, bool success, bool allow_restart) { return; fail: - log_warning("%s failed to run install restart timer: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run install restart timer: %s", unit_id(UNIT(s)), strerror(-r)); service_enter_dead(s, false, false); } @@ -407,7 +407,7 @@ static void service_enter_stop_post(Service *s, bool success) { return; fail: - log_warning("%s failed to run stop executable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run stop executable: %s", unit_id(UNIT(s)), strerror(-r)); service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); } @@ -450,7 +450,7 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { return; fail: - log_warning("%s failed to kill processes: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to kill processes: %s", unit_id(UNIT(s)), strerror(-r)); if (sent) { s->failure = true; @@ -480,7 +480,7 @@ static void service_enter_stop(Service *s, bool success) { return; fail: - log_warning("%s failed to run stop executable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run stop executable: %s", unit_id(UNIT(s)), strerror(-r)); service_enter_signal(s, SERVICE_STOP_SIGTERM, false); } @@ -500,7 +500,7 @@ static void service_enter_start_post(Service *s) { return; fail: - log_warning("%s failed to run start-post executable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run start-post executable: %s", unit_id(UNIT(s)), strerror(-r)); service_enter_stop(s, false); } @@ -538,7 +538,7 @@ static void service_enter_start(Service *s) { return; fail: - log_warning("%s failed to run start exectuable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run start exectuable: %s", unit_id(UNIT(s)), strerror(-r)); service_enter_stop(s, false); } @@ -559,7 +559,7 @@ static void service_enter_start_pre(Service *s) { return; fail: - log_warning("%s failed to run start-pre executable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run start-pre executable: %s", unit_id(UNIT(s)), strerror(-r)); service_enter_dead(s, false, true); } @@ -567,16 +567,16 @@ static void service_enter_restart(Service *s) { int r; assert(s); - if ((r = manager_add_job(NAME(s)->meta.manager, JOB_START, NAME(s), JOB_FAIL, false, NULL)) < 0) + if ((r = manager_add_job(UNIT(s)->meta.manager, JOB_START, UNIT(s), JOB_FAIL, false, NULL)) < 0) goto fail; - log_debug("%s scheduled restart job.", name_id(NAME(s))); + log_debug("%s scheduled restart job.", unit_id(UNIT(s))); service_enter_dead(s, true, false); return; fail: - log_warning("%s failed to schedule restart job: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to schedule restart job: %s", unit_id(UNIT(s)), strerror(-r)); service_enter_dead(s, false, false); } @@ -597,7 +597,7 @@ static void service_enter_reload(Service *s) { return; fail: - log_warning("%s failed to run reload executable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run reload executable: %s", unit_id(UNIT(s)), strerror(-r)); service_enter_stop(s, false); } @@ -619,7 +619,7 @@ static void service_run_next(Service *s, bool success) { return; fail: - log_warning("%s failed to run spawn next executable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run spawn next executable: %s", unit_id(UNIT(s)), strerror(-r)); if (s->state == SERVICE_STOP) service_enter_stop_post(s, false); @@ -629,8 +629,8 @@ fail: service_enter_stop(s, false); } -static int service_start(Name *n) { - Service *s = SERVICE(n); +static int service_start(Unit *u) { + Service *s = SERVICE(u); assert(s); @@ -659,8 +659,8 @@ static int service_start(Name *n) { return 0; } -static int service_stop(Name *n) { - Service *s = SERVICE(n); +static int service_stop(Unit *u) { + Service *s = SERVICE(u); assert(s); @@ -681,8 +681,8 @@ static int service_stop(Name *n) { return 0; } -static int service_reload(Name *n) { - Service *s = SERVICE(n); +static int service_reload(Unit *u) { + Service *s = SERVICE(u); assert(s); @@ -692,18 +692,18 @@ static int service_reload(Name *n) { return 0; } -static bool service_can_reload(Name *n) { - Service *s = SERVICE(n); +static bool service_can_reload(Unit *u) { + Service *s = SERVICE(u); assert(s); return !!s->exec_command[SERVICE_EXEC_RELOAD]; } -static NameActiveState service_active_state(Name *n) { - assert(n); +static UnitActiveState service_active_state(Unit *u) { + assert(u); - return state_table[SERVICE(n)->state]; + return state_table[SERVICE(u)->state]; } static int main_pid_good(Service *s) { @@ -727,8 +727,8 @@ static bool control_pid_good(Service *s) { return s->control_pid > 0; } -static void service_sigchld_event(Name *n, pid_t pid, int code, int status) { - Service *s = SERVICE(n); +static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { + Service *s = SERVICE(u); bool success; assert(s); @@ -747,7 +747,7 @@ static void service_sigchld_event(Name *n, pid_t pid, int code, int status) { s->exec_command[SERVICE_EXEC_START]->exec_status = s->main_exec_status; } - log_debug("%s: main process exited, code=%s status=%i", name_id(n), sigchld_code(code), status); + log_debug("%s: main process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status); /* The service exited, so the service is officially * gone. */ @@ -784,7 +784,7 @@ static void service_sigchld_event(Name *n, pid_t pid, int code, int status) { exec_status_fill(&s->control_command->exec_status, pid, code, status); s->control_pid = 0; - log_debug("%s: control process exited, code=%s status=%i", name_id(n), sigchld_code(code), status); + log_debug("%s: control process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status); /* If we are shutting things down anyway we * don't care about failing commands. */ @@ -840,7 +840,7 @@ static void service_sigchld_event(Name *n, pid_t pid, int code, int status) { * executed. */ if ((r = service_load_pid_file(s)) < 0) - log_warning("%s: failed to load PID file %s: %s", name_id(NAME(s)), s->pid_file, strerror(-r)); + log_warning("%s: failed to load PID file %s: %s", unit_id(UNIT(s)), s->pid_file, strerror(-r)); } /* Fall through */ @@ -888,8 +888,8 @@ static void service_sigchld_event(Name *n, pid_t pid, int code, int status) { assert_not_reached("Got SIGCHLD for unkown PID"); } -static void service_timer_event(Name *n, int id, uint64_t elapsed) { - Service *s = SERVICE(n); +static void service_timer_event(Unit *u, int id, uint64_t elapsed) { + Service *s = SERVICE(u); assert(s); assert(elapsed == 1); @@ -902,17 +902,17 @@ static void service_timer_event(Name *n, int id, uint64_t elapsed) { case SERVICE_START: case SERVICE_START_POST: case SERVICE_RELOAD: - log_warning("%s operation timed out. Stopping.", name_id(n)); + log_warning("%s operation timed out. Stopping.", unit_id(u)); service_enter_stop(s, false); break; case SERVICE_STOP: - log_warning("%s stopping timed out. Terminating.", name_id(n)); + log_warning("%s stopping timed out. Terminating.", unit_id(u)); service_enter_signal(s, SERVICE_STOP_SIGTERM, false); break; case SERVICE_STOP_SIGTERM: - log_warning("%s stopping timed out. Killing.", name_id(n)); + log_warning("%s stopping timed out. Killing.", unit_id(u)); service_enter_signal(s, SERVICE_STOP_SIGKILL, false); break; @@ -921,27 +921,27 @@ static void service_timer_event(Name *n, int id, uint64_t elapsed) { * Must be something we cannot kill, so let's just be * weirded out and continue */ - log_warning("%s still around after SIGKILL. Ignoring.", name_id(n)); + log_warning("%s still around after SIGKILL. Ignoring.", unit_id(u)); service_enter_stop_post(s, false); break; case SERVICE_STOP_POST: - log_warning("%s stopping timed out (2). Terminating.", name_id(n)); + log_warning("%s stopping timed out (2). Terminating.", unit_id(u)); service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); break; case SERVICE_FINAL_SIGTERM: - log_warning("%s stopping timed out (2). Killing.", name_id(n)); + log_warning("%s stopping timed out (2). Killing.", unit_id(u)); service_enter_signal(s, SERVICE_FINAL_SIGKILL, false); break; case SERVICE_FINAL_SIGKILL: - log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", name_id(n)); + log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", unit_id(u)); service_enter_dead(s, false, true); break; case SERVICE_AUTO_RESTART: - log_debug("%s holdoff time over, scheduling restart.", name_id(n)); + log_debug("%s holdoff time over, scheduling restart.", unit_id(u)); service_enter_restart(s); break; @@ -950,7 +950,7 @@ static void service_timer_event(Name *n, int id, uint64_t elapsed) { } } -const NameVTable service_vtable = { +const UnitVTable service_vtable = { .suffix = ".service", .init = service_init, diff --git a/service.h b/service.h index 41dabff5..17bcd2aa 100644 --- a/service.h +++ b/service.h @@ -5,7 +5,7 @@ typedef struct Service Service; -#include "name.h" +#include "unit.h" typedef enum ServiceState { SERVICE_DEAD, @@ -73,6 +73,6 @@ struct Service { int timer_id; }; -const NameVTable service_vtable; +const UnitVTable service_vtable; #endif diff --git a/snapshot.c b/snapshot.c index 8fd819e2..3e550752 100644 --- a/snapshot.c +++ b/snapshot.c @@ -1,21 +1,21 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ -#include "name.h" +#include "unit.h" #include "snapshot.h" -static void snapshot_done(Name *n) { - Snapshot *s = SNAPSHOT(n); +static void snapshot_done(Unit *u) { + Snapshot *s = SNAPSHOT(u); assert(s); /* Nothing here for now */ } -static NameActiveState snapshot_active_state(Name *n) { - return SNAPSHOT(n)->state == SNAPSHOT_DEAD ? NAME_INACTIVE : NAME_ACTIVE; +static UnitActiveState snapshot_active_state(Unit *u) { + return SNAPSHOT(u)->state == SNAPSHOT_DEAD ? UNIT_INACTIVE : UNIT_ACTIVE; } -const NameVTable snapshot_vtable = { +const UnitVTable snapshot_vtable = { .suffix = ".snapshot", .done = snapshot_done, diff --git a/snapshot.h b/snapshot.h index 8d35e174..563af412 100644 --- a/snapshot.h +++ b/snapshot.h @@ -5,7 +5,7 @@ typedef struct Snapshot Snapshot; -#include "name.h" +#include "unit.h" typedef enum SnapshotState { SNAPSHOT_DEAD, @@ -19,6 +19,6 @@ struct Snapshot { bool cleanup:1; }; -extern const NameVTable snapshot_vtable; +extern const UnitVTable snapshot_vtable; #endif diff --git a/socket.c b/socket.c index 23e347d7..3d02f414 100644 --- a/socket.c +++ b/socket.c @@ -8,27 +8,27 @@ #include #include -#include "name.h" +#include "unit.h" #include "socket.h" #include "log.h" -static const NameActiveState state_table[_SOCKET_STATE_MAX] = { - [SOCKET_DEAD] = NAME_INACTIVE, - [SOCKET_START_PRE] = NAME_ACTIVATING, - [SOCKET_START_POST] = NAME_ACTIVATING, - [SOCKET_LISTENING] = NAME_ACTIVE, - [SOCKET_RUNNING] = NAME_ACTIVE, - [SOCKET_STOP_PRE] = NAME_DEACTIVATING, - [SOCKET_STOP_PRE_SIGTERM] = NAME_DEACTIVATING, - [SOCKET_STOP_PRE_SIGKILL] = NAME_DEACTIVATING, - [SOCKET_STOP_POST] = NAME_DEACTIVATING, - [SOCKET_STOP_POST_SIGTERM] = NAME_DEACTIVATING, - [SOCKET_STOP_POST_SIGKILL] = NAME_DEACTIVATING, - [SOCKET_MAINTAINANCE] = NAME_INACTIVE, +static const UnitActiveState state_table[_SOCKET_STATE_MAX] = { + [SOCKET_DEAD] = UNIT_INACTIVE, + [SOCKET_START_PRE] = UNIT_ACTIVATING, + [SOCKET_START_POST] = UNIT_ACTIVATING, + [SOCKET_LISTENING] = UNIT_ACTIVE, + [SOCKET_RUNNING] = UNIT_ACTIVE, + [SOCKET_STOP_PRE] = UNIT_DEACTIVATING, + [SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING, + [SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING, + [SOCKET_STOP_POST] = UNIT_DEACTIVATING, + [SOCKET_STOP_POST_SIGTERM] = UNIT_DEACTIVATING, + [SOCKET_STOP_POST_SIGKILL] = UNIT_DEACTIVATING, + [SOCKET_MAINTAINANCE] = UNIT_INACTIVE, }; -static void socket_done(Name *n) { - Socket *s = SOCKET(n); +static void socket_done(Unit *u) { + Socket *s = SOCKET(u); SocketPort *p; assert(s); @@ -47,17 +47,17 @@ static void socket_done(Name *n) { s->control_command = NULL; if (s->control_pid > 0) { - name_unwatch_pid(n, s->control_pid); + unit_unwatch_pid(u, s->control_pid); s->control_pid = 0; } s->service = NULL; - name_unwatch_timer(n, &s->timer_id); + unit_unwatch_timer(u, &s->timer_id); } -static int socket_init(Name *n) { - Socket *s = SOCKET(n); +static int socket_init(Unit *u) { + Socket *s = SOCKET(u); char *t; int r; @@ -71,27 +71,27 @@ static int socket_init(Name *n) { s->timeout_usec = DEFAULT_TIMEOUT_USEC; exec_context_init(&s->exec_context); - if ((r = name_load_fragment_and_dropin(n)) < 0) + if ((r = unit_load_fragment_and_dropin(u)) < 0) goto fail; - if (!(t = name_change_suffix(name_id(n), ".service"))) { + if (!(t = unit_name_change_suffix(unit_id(u), ".service"))) { r = -ENOMEM; goto fail; } - r = manager_load_name(n->meta.manager, t, (Name**) &s->service); + r = manager_load_unit(u->meta.manager, t, (Unit**) &s->service); free(t); if (r < 0) goto fail; - if ((r = name_add_dependency(n, NAME_BEFORE, NAME(s->service))) < 0) + if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0) goto fail; return 0; fail: - socket_done(n); + socket_done(u); return r; } @@ -108,7 +108,7 @@ static const char* listen_lookup(int type) { return NULL; } -static void socket_dump(Name *n, FILE *f, const char *prefix) { +static void socket_dump(Unit *u, FILE *f, const char *prefix) { static const char* const state_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = "dead", @@ -133,7 +133,7 @@ static void socket_dump(Name *n, FILE *f, const char *prefix) { }; SocketExecCommand c; - Socket *s = SOCKET(n); + Socket *s = SOCKET(u); SocketPort *p; assert(s); @@ -183,7 +183,7 @@ static void socket_close_fds(Socket *s) { if (p->fd < 0) continue; - name_unwatch_fd(NAME(s), p->fd); + unit_unwatch_fd(UNIT(s), p->fd); assert_se(close_nointr(p->fd) >= 0); p->fd = -1; @@ -250,7 +250,7 @@ static void socket_unwatch_fds(Socket *s) { if (p->fd < 0) continue; - name_unwatch_fd(NAME(s), p->fd); + unit_unwatch_fd(UNIT(s), p->fd); } } @@ -264,7 +264,7 @@ static int socket_watch_fds(Socket *s) { if (p->fd < 0) continue; - if ((r = name_watch_fd(NAME(s), p->fd, POLLIN)) < 0) + if ((r = unit_watch_fd(UNIT(s), p->fd, POLLIN)) < 0) goto fail; } @@ -290,7 +290,7 @@ static void socket_set_state(Socket *s, SocketState state) { state != SOCKET_STOP_POST && state != SOCKET_STOP_POST_SIGTERM && state != SOCKET_STOP_POST_SIGKILL) - name_unwatch_timer(NAME(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_id); if (state != SOCKET_START_PRE && state != SOCKET_START_POST && @@ -301,7 +301,7 @@ static void socket_set_state(Socket *s, SocketState state) { state != SOCKET_STOP_POST_SIGTERM && state != SOCKET_STOP_POST_SIGKILL) if (s->control_pid >= 0) { - name_unwatch_pid(NAME(s), s->control_pid); + unit_unwatch_pid(UNIT(s), s->control_pid); s->control_pid = 0; } @@ -322,7 +322,7 @@ static void socket_set_state(Socket *s, SocketState state) { if (state != SOCKET_LISTENING) socket_unwatch_fds(s); - name_notify(NAME(s), state_table[old_state], state_table[s->state]); + unit_notify(UNIT(s), state_table[old_state], state_table[s->state]); } static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) { @@ -334,15 +334,15 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) { assert(_pid); if (timeout) { - if ((r = name_watch_timer(NAME(s), s->timeout_usec, &s->timer_id)) < 0) + if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_id)) < 0) goto fail; } else - name_unwatch_timer(NAME(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_id); if ((r = exec_spawn(c, &s->exec_context, NULL, 0, &pid)) < 0) goto fail; - if ((r = name_watch_pid(NAME(s), pid)) < 0) + if ((r = unit_watch_pid(UNIT(s), pid)) < 0) /* FIXME: we need to do something here */ goto fail; @@ -352,7 +352,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) { fail: if (timeout) - name_unwatch_timer(NAME(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_id); return r; } @@ -385,7 +385,7 @@ static void socket_enter_stop_post(Socket *s, bool success) { return; fail: - log_warning("%s failed to run stop-post executable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run stop-post executable: %s", unit_id(UNIT(s)), strerror(-r)); socket_enter_dead(s, false); } @@ -414,7 +414,7 @@ static void socket_enter_signal(Socket *s, SocketState state, bool success) { return; fail: - log_warning("%s failed to kill processes: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to kill processes: %s", unit_id(UNIT(s)), strerror(-r)); if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) socket_enter_stop_post(s, false); @@ -441,7 +441,7 @@ static void socket_enter_stop_pre(Socket *s, bool success) { return; fail: - log_warning("%s failed to run stop-pre executable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run stop-pre executable: %s", unit_id(UNIT(s)), strerror(-r)); socket_enter_stop_post(s, false); } @@ -451,14 +451,14 @@ static void socket_enter_start_post(Socket *s) { if ((r = socket_open_fds(s)) < 0 || (r = socket_watch_fds(s)) < 0) { - log_warning("%s failed to listen on sockets: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to listen on sockets: %s", unit_id(UNIT(s)), strerror(-r)); goto fail; } if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) { if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0) { - log_warning("%s failed to run start-post executable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run start-post executable: %s", unit_id(UNIT(s)), strerror(-r)); goto fail; } @@ -488,7 +488,7 @@ static void socket_enter_start_pre(Socket *s) { return; fail: - log_warning("%s failed to run start-pre exectuable: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to run start-pre exectuable: %s", unit_id(UNIT(s)), strerror(-r)); socket_enter_dead(s, false); } @@ -497,14 +497,14 @@ static void socket_enter_running(Socket *s) { assert(s); - if ((r = manager_add_job(NAME(s)->meta.manager, JOB_START, NAME(s->service), JOB_REPLACE, true, NULL)) < 0) + if ((r = manager_add_job(UNIT(s)->meta.manager, JOB_START, UNIT(s->service), JOB_REPLACE, true, NULL)) < 0) goto fail; socket_set_state(s, SOCKET_RUNNING); return; fail: - log_warning("%s failed to queue socket startup job: %s", name_id(NAME(s)), strerror(-r)); + log_warning("%s failed to queue socket startup job: %s", unit_id(UNIT(s)), strerror(-r)); socket_enter_dead(s, false); } @@ -534,8 +534,8 @@ fail: socket_enter_stop_pre(s, false); } -static int socket_start(Name *n) { - Socket *s = SOCKET(n); +static int socket_start(Unit *u) { + Socket *s = SOCKET(u); assert(s); @@ -554,7 +554,7 @@ static int socket_start(Name *n) { return 0; /* Cannot run this without the service being around */ - if (s->service->meta.load_state != NAME_LOADED) + if (s->service->meta.load_state != UNIT_LOADED) return -ENOENT; assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE); @@ -564,8 +564,8 @@ static int socket_start(Name *n) { return 0; } -static int socket_stop(Name *n) { - Socket *s = SOCKET(n); +static int socket_stop(Unit *u) { + Socket *s = SOCKET(u); assert(s); @@ -581,18 +581,18 @@ static int socket_stop(Name *n) { return 0; } -static NameActiveState socket_active_state(Name *n) { - assert(n); +static UnitActiveState socket_active_state(Unit *u) { + assert(u); - return state_table[SOCKET(n)->state]; + return state_table[SOCKET(u)->state]; } -static void socket_fd_event(Name *n, int fd, uint32_t events) { - Socket *s = SOCKET(n); +static void socket_fd_event(Unit *u, int fd, uint32_t events) { + Socket *s = SOCKET(u); assert(s); - log_info("Incoming traffic on %s", name_id(n)); + log_info("Incoming traffic on %s", unit_id(u)); if (events != POLLIN) socket_enter_stop_pre(s, false); @@ -600,8 +600,8 @@ static void socket_fd_event(Name *n, int fd, uint32_t events) { socket_enter_running(s); } -static void socket_sigchld_event(Name *n, pid_t pid, int code, int status) { - Socket *s = SOCKET(n); +static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { + Socket *s = SOCKET(u); bool success; assert(s); @@ -616,7 +616,7 @@ static void socket_sigchld_event(Name *n, pid_t pid, int code, int status) { exec_status_fill(&s->control_command->exec_status, pid, code, status); s->control_pid = 0; - log_debug("%s: control process exited, code=%s status=%i", name_id(n), sigchld_code(code), status); + log_debug("%s: control process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status); if (s->control_command->command_next && (success || (s->state == SOCKET_EXEC_STOP_PRE || s->state == SOCKET_EXEC_STOP_POST))) @@ -659,8 +659,8 @@ static void socket_sigchld_event(Name *n, pid_t pid, int code, int status) { } } -static void socket_timer_event(Name *n, int id, uint64_t elapsed) { - Socket *s = SOCKET(n); +static void socket_timer_event(Unit *u, int id, uint64_t elapsed) { + Socket *s = SOCKET(u); assert(s); assert(elapsed == 1); @@ -671,37 +671,37 @@ static void socket_timer_event(Name *n, int id, uint64_t elapsed) { case SOCKET_START_PRE: case SOCKET_START_POST: - log_warning("%s operation timed out. Stopping.", name_id(n)); + log_warning("%s operation timed out. Stopping.", unit_id(u)); socket_enter_stop_pre(s, false); break; case SOCKET_STOP_PRE: - log_warning("%s stopping timed out. Terminating.", name_id(n)); + log_warning("%s stopping timed out. Terminating.", unit_id(u)); socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, false); break; case SOCKET_STOP_PRE_SIGTERM: - log_warning("%s stopping timed out. Killing.", name_id(n)); + log_warning("%s stopping timed out. Killing.", unit_id(u)); socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, false); break; case SOCKET_STOP_PRE_SIGKILL: - log_warning("%s still around after SIGKILL. Ignoring.", name_id(n)); + log_warning("%s still around after SIGKILL. Ignoring.", unit_id(u)); socket_enter_stop_post(s, false); break; case SOCKET_STOP_POST: - log_warning("%s stopping timed out (2). Terminating.", name_id(n)); + log_warning("%s stopping timed out (2). Terminating.", unit_id(u)); socket_enter_signal(s, SOCKET_STOP_POST_SIGTERM, false); break; case SOCKET_STOP_POST_SIGTERM: - log_warning("%s stopping timed out (2). Killing.", name_id(n)); + log_warning("%s stopping timed out (2). Killing.", unit_id(u)); socket_enter_signal(s, SOCKET_STOP_POST_SIGKILL, false); break; case SOCKET_STOP_POST_SIGKILL: - log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", name_id(n)); + log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", unit_id(u)); socket_enter_dead(s, false); break; @@ -742,7 +742,7 @@ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { return 0; } -const NameVTable socket_vtable = { +const UnitVTable socket_vtable = { .suffix = ".socket", .init = socket_init, diff --git a/socket.h b/socket.h index 1e651c6d..c34689b8 100644 --- a/socket.h +++ b/socket.h @@ -5,7 +5,7 @@ typedef struct Socket Socket; -#include "name.h" +#include "unit.h" #include "socket-util.h" typedef enum SocketState { @@ -78,6 +78,6 @@ struct Socket { /* Called from the service code when collecting fds */ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds); -extern const NameVTable socket_vtable; +extern const UnitVTable socket_vtable; #endif diff --git a/target.c b/target.c index 12a21929..6754676b 100644 --- a/target.c +++ b/target.c @@ -1,25 +1,25 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ -#include "name.h" +#include "unit.h" #include "target.h" #include "load-fragment.h" -static void target_done(Name *n) { - Target *m = TARGET(n); +static void target_done(Unit *u) { + Target *m = TARGET(u); assert(m); /* Nothing here for now */ } -static NameActiveState target_active_state(Name *n) { - return TARGET(n)->state == TARGET_DEAD ? NAME_INACTIVE : NAME_ACTIVE; +static UnitActiveState target_active_state(Unit *u) { + return TARGET(u)->state == TARGET_DEAD ? UNIT_INACTIVE : UNIT_ACTIVE; } -const NameVTable target_vtable = { +const UnitVTable target_vtable = { .suffix = ".target", - .init = name_load_fragment, + .init = unit_load_fragment, .done = target_done, .active_state = target_active_state diff --git a/target.h b/target.h index 61f2803c..16cd9c44 100644 --- a/target.h +++ b/target.h @@ -5,7 +5,7 @@ typedef struct Target Target; -#include "name.h" +#include "unit.h" typedef enum TargetState { TARGET_DEAD, @@ -18,6 +18,6 @@ struct Target { TargetState state; }; -extern const NameVTable target_vtable; +extern const UnitVTable target_vtable; #endif diff --git a/test-engine.c b/test-engine.c index 53cac92e..c14a3402 100644 --- a/test-engine.c +++ b/test-engine.c @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) { Manager *m = NULL; - Name *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL; + Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL; Job *j; assert_se(chdir("test2") == 0); @@ -17,10 +17,10 @@ int main(int argc, char *argv[]) { assert_se(m = manager_new()); printf("Load1:\n"); - assert_se(manager_load_name(m, "a.service", &a) == 0); - assert_se(manager_load_name(m, "b.service", &b) == 0); - assert_se(manager_load_name(m, "c.service", &c) == 0); - manager_dump_names(m, stdout, "\t"); + assert_se(manager_load_unit(m, "a.service", &a) == 0); + assert_se(manager_load_unit(m, "b.service", &b) == 0); + assert_se(manager_load_unit(m, "c.service", &c) == 0); + manager_dump_units(m, stdout, "\t"); printf("Test1: (Trivial)\n"); assert_se(manager_add_job(m, JOB_START, c, JOB_REPLACE, false, &j) == 0); @@ -28,9 +28,9 @@ int main(int argc, char *argv[]) { printf("Load2:\n"); manager_clear_jobs(m); - assert_se(manager_load_name(m, "d.service", &d) == 0); - assert_se(manager_load_name(m, "e.service", &e) == 0); - manager_dump_names(m, stdout, "\t"); + assert_se(manager_load_unit(m, "d.service", &d) == 0); + assert_se(manager_load_unit(m, "e.service", &e) == 0); + manager_dump_units(m, stdout, "\t"); printf("Test2: (Cyclic Order, Unfixable)\n"); assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, &j) == -ENOEXEC); @@ -45,8 +45,8 @@ int main(int argc, char *argv[]) { manager_dump_jobs(m, stdout, "\t"); printf("Load3:\n"); - assert_se(manager_load_name(m, "g.service", &g) == 0); - manager_dump_names(m, stdout, "\t"); + assert_se(manager_load_unit(m, "g.service", &g) == 0); + manager_dump_units(m, stdout, "\t"); printf("Test5: (Colliding transaction, fail)\n"); assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, false, &j) == -EEXIST); @@ -67,8 +67,8 @@ int main(int argc, char *argv[]) { manager_dump_jobs(m, stdout, "\t"); printf("Load4:\n"); - assert_se(manager_load_name(m, "h.service", &h) == 0); - manager_dump_names(m, stdout, "\t"); + assert_se(manager_load_unit(m, "h.service", &h) == 0); + manager_dump_units(m, stdout, "\t"); printf("Test10: (Unmeargable job type of auxiliary job, fail)\n"); assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, false, &j) == 0); diff --git a/timer.c b/timer.c index 0c891368..29346204 100644 --- a/timer.c +++ b/timer.c @@ -1,29 +1,29 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ -#include "name.h" +#include "unit.h" #include "timer.h" -static void timer_done(Name *n) { - Timer *t = TIMER(n); +static void timer_done(Unit *u) { + Timer *t = TIMER(u); assert(t); } -static NameActiveState timer_active_state(Name *n) { +static UnitActiveState timer_active_state(Unit *u) { - static const NameActiveState table[_TIMER_STATE_MAX] = { - [TIMER_DEAD] = NAME_INACTIVE, - [TIMER_WAITING] = NAME_ACTIVE, - [TIMER_RUNNING] = NAME_ACTIVE + static const UnitActiveState table[_TIMER_STATE_MAX] = { + [TIMER_DEAD] = UNIT_INACTIVE, + [TIMER_WAITING] = UNIT_ACTIVE, + [TIMER_RUNNING] = UNIT_ACTIVE }; - return table[TIMER(n)->state]; + return table[TIMER(u)->state]; } -const NameVTable timer_vtable = { +const UnitVTable timer_vtable = { .suffix = ".timer", - .init = name_load_fragment_and_dropin, + .init = unit_load_fragment_and_dropin, .done = timer_done, .active_state = timer_active_state diff --git a/timer.h b/timer.h index fcbddc0a..ff9f73ce 100644 --- a/timer.h +++ b/timer.h @@ -5,7 +5,7 @@ typedef struct Timer Timer; -#include "name.h" +#include "unit.h" typedef enum TimerState { TIMER_DEAD, @@ -25,6 +25,6 @@ struct Timer { Service *service; }; -const NameVTable timer_vtable; +const UnitVTable timer_vtable; #endif diff --git a/unit.c b/unit.c new file mode 100644 index 00000000..15401e24 --- /dev/null +++ b/unit.c @@ -0,0 +1,807 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include +#include +#include +#include +#include +#include + +#include "set.h" +#include "unit.h" +#include "macro.h" +#include "strv.h" +#include "load-fragment.h" +#include "load-dropin.h" +#include "log.h" + +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, + [UNIT_SNAPSHOT] = &snapshot_vtable +}; + +UnitType unit_name_to_type(const char *n) { + UnitType t; + + assert(n); + + for (t = 0; t < _UNIT_TYPE_MAX; t++) + if (endswith(n, unit_vtable[t]->suffix)) + return t; + + return _UNIT_TYPE_INVALID; +} + +#define VALID_CHARS \ + "0123456789" \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "-_" + +bool unit_name_is_valid(const char *n) { + UnitType t; + const char *e, *i; + + assert(n); + + if (strlen(n) >= UNIT_NAME_MAX) + return false; + + t = unit_name_to_type(n); + if (t < 0 || t >= _UNIT_TYPE_MAX) + return false; + + if (!(e = strrchr(n, '.'))) + return false; + + for (i = n; i < e; i++) + if (!strchr(VALID_CHARS, *i)) + return false; + + return true; +} + +char *unit_name_change_suffix(const char *n, const char *suffix) { + char *e, *r; + size_t a, b; + + assert(n); + assert(unit_name_is_valid(n)); + assert(suffix); + + assert_se(e = strrchr(n, '.')); + a = e - n; + b = strlen(suffix); + + if (!(r = new(char, a + b + 1))) + return NULL; + + memcpy(r, n, a); + memcpy(r+a, suffix, b+1); + + return r; +} + +Unit *unit_new(Manager *m) { + Unit *u; + + assert(m); + + if (!(u = new0(Unit, 1))) + return NULL; + + if (!(u->meta.names = set_new(string_hash_func, string_compare_func))) { + free(u); + return NULL; + } + + u->meta.manager = m; + u->meta.type = _UNIT_TYPE_INVALID; + + return u; +} + +int unit_add_name(Unit *u, const char *text) { + UnitType t; + char *s; + int r; + + assert(u); + assert(text); + + if (!unit_name_is_valid(text)) + return -EINVAL; + + if ((t = unit_name_to_type(text)) == _UNIT_TYPE_INVALID) + return -EINVAL; + + if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type) + return -EINVAL; + + if (!(s = strdup(text))) + return -ENOMEM; + + if ((r = set_put(u->meta.names, s)) < 0) { + free(s); + + if (r == -EEXIST) + return 0; + + return r; + } + + if ((r = hashmap_put(u->meta.manager->units, s, u)) < 0) { + set_remove(u->meta.names, s); + free(s); + return r; + } + + u->meta.type = t; + + if (!u->meta.id) + u->meta.id = s; + + return 0; +} + +void unit_add_to_load_queue(Unit *u) { + assert(u); + + if (u->meta.load_state != UNIT_STUB || u->meta.in_load_queue) + return; + + LIST_PREPEND(Meta, load_queue, u->meta.manager->load_queue, &u->meta); + u->meta.in_load_queue = true; +} + +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->meta.dependencies[d], u); + } + + set_free(s); +} + +void unit_free(Unit *u) { + UnitDependency d; + Iterator i; + char *t; + + assert(u); + + /* Detach from next 'bigger' objects */ + + SET_FOREACH(t, u->meta.names, i) + hashmap_remove_value(u->meta.manager->units, t, u); + + if (u->meta.in_load_queue) + LIST_REMOVE(Meta, load_queue, u->meta.manager->load_queue, &u->meta); + + if (u->meta.load_state == UNIT_LOADED) + if (UNIT_VTABLE(u)->done) + UNIT_VTABLE(u)->done(u); + + /* Free data and next 'smaller' objects */ + if (u->meta.job) + job_free(u->meta.job); + + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) + bidi_set_free(u, u->meta.dependencies[d]); + + free(u->meta.description); + + while ((t = set_steal_first(u->meta.names))) + free(t); + set_free(u->meta.names); + + free(u); +} + +UnitActiveState unit_active_state(Unit *u) { + assert(u); + + if (u->meta.load_state != UNIT_LOADED) + return UNIT_INACTIVE; + + return UNIT_VTABLE(u)->active_state(u); +} + +static int ensure_merge(Set **s, Set *other) { + + if (!other) + return 0; + + if (*s) + return set_merge(*s, other); + + if (!(*s = set_copy(other))) + return -ENOMEM; + + return 0; +} + +/* FIXME: Does not rollback on failure! */ +int unit_merge(Unit *u, Unit *other) { + int r; + UnitDependency d; + + assert(u); + assert(other); + assert(u->meta.manager == other->meta.manager); + + /* 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; + + /* Merge names */ + if ((r = ensure_merge(&u->meta.names, other->meta.names)) < 0) + return r; + + /* 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; + + return 0; +} + +const char* unit_id(Unit *u) { + assert(u); + + if (u->meta.id) + return u->meta.id; + + return set_first(u->meta.names); +} + +const char *unit_description(Unit *u) { + assert(u); + + if (u->meta.description) + return u->meta.description; + + return unit_id(u); +} + +void unit_dump(Unit *u, FILE *f, const char *prefix) { + + static const char* const load_state_table[_UNIT_LOAD_STATE_MAX] = { + [UNIT_STUB] = "stub", + [UNIT_LOADED] = "loaded", + [UNIT_FAILED] = "failed" + }; + + static const char* const active_state_table[_UNIT_ACTIVE_STATE_MAX] = { + [UNIT_ACTIVE] = "active", + [UNIT_INACTIVE] = "inactive", + [UNIT_ACTIVATING] = "activating", + [UNIT_DEACTIVATING] = "deactivating" + }; + + static const char* const dependency_table[_UNIT_DEPENDENCY_MAX] = { + [UNIT_REQUIRES] = "Requires", + [UNIT_SOFT_REQUIRES] = "SoftRequires", + [UNIT_WANTS] = "Wants", + [UNIT_REQUISITE] = "Requisite", + [UNIT_SOFT_REQUISITE] = "SoftRequisite", + [UNIT_REQUIRED_BY] = "RequiredBy", + [UNIT_SOFT_REQUIRED_BY] = "SoftRequiredBy", + [UNIT_WANTED_BY] = "WantedBy", + [UNIT_CONFLICTS] = "Conflicts", + [UNIT_BEFORE] = "Before", + [UNIT_AFTER] = "After", + }; + + char *t; + UnitDependency d; + Iterator i; + char *prefix2; + + assert(u); + + if (!prefix) + prefix = ""; + prefix2 = strappend(prefix, "\t"); + if (!prefix2) + prefix2 = ""; + + fprintf(f, + "%s→ Unit %s:\n" + "%s\tDescription: %s\n" + "%s\tUnit Load State: %s\n" + "%s\tUnit Active State: %s\n", + prefix, unit_id(u), + prefix, unit_description(u), + prefix, load_state_table[u->meta.load_state], + prefix, active_state_table[unit_active_state(u)]); + + SET_FOREACH(t, u->meta.names, i) + fprintf(f, "%s\tName: %s\n", prefix, t); + + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { + Unit *other; + + if (set_isempty(u->meta.dependencies[d])) + continue; + + SET_FOREACH(other, u->meta.dependencies[d], i) + fprintf(f, "%s\t%s: %s\n", prefix, dependency_table[d], unit_id(other)); + } + + if (UNIT_VTABLE(u)->dump) + UNIT_VTABLE(u)->dump(u, f, prefix2); + + if (u->meta.job) + job_dump(u->meta.job, f, prefix2); + + free(prefix2); +} + +/* Common implementation for multiple backends */ +int unit_load_fragment_and_dropin(Unit *u) { + int r; + + assert(u); + + /* Load a .socket file */ + if ((r = unit_load_fragment(u)) < 0) + return r; + + /* Load drop-in directory data */ + if ((r = unit_load_dropin(u)) < 0) + return r; + + return 0; +} + +int unit_load(Unit *u) { + int r; + + assert(u); + + if (u->meta.in_load_queue) { + LIST_REMOVE(Meta, load_queue, u->meta.manager->load_queue, &u->meta); + u->meta.in_load_queue = false; + } + + if (u->meta.load_state != UNIT_STUB) + return 0; + + if (UNIT_VTABLE(u)->init) + if ((r = UNIT_VTABLE(u)->init(u)) < 0) + goto fail; + + u->meta.load_state = UNIT_LOADED; + return 0; + +fail: + u->meta.load_state = UNIT_FAILED; + return r; +} + +/* Errors: + * -EBADR: This unit type does not support starting. + * -EALREADY: Unit is already started. + * -EAGAIN: An operation is already in progress. Retry later. + */ +int unit_start(Unit *u) { + UnitActiveState state; + + assert(u); + + if (!UNIT_VTABLE(u)->start) + return -EBADR; + + state = unit_active_state(u); + if (UNIT_IS_ACTIVE_OR_RELOADING(state)) + return -EALREADY; + + /* 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. */ + + return UNIT_VTABLE(u)->start(u); +} + +bool unit_can_start(Unit *u) { + assert(u); + + return !!UNIT_VTABLE(u)->start; +} + +/* 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; + + assert(u); + + if (!UNIT_VTABLE(u)->stop) + return -EBADR; + + state = unit_active_state(u); + if (state == UNIT_INACTIVE) + return -EALREADY; + + if (state == UNIT_DEACTIVATING) + return 0; + + 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; + + assert(u); + + if (!unit_can_reload(u)) + return -EBADR; + + state = unit_active_state(u); + if (unit_active_state(u) == UNIT_ACTIVE_RELOADING) + return -EALREADY; + + if (unit_active_state(u) != UNIT_ACTIVE) + return -ENOEXEC; + + 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 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->meta.dependencies[UNIT_REQUIRES], i) + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) + manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); + + SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRES], i) + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) + manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); + + SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE], i) + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) + manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); + + SET_FOREACH(other, u->meta.dependencies[UNIT_WANTS], i) + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) + manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); + + SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTS], i) + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) + manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); +} + +static void retroactively_stop_dependencies(Unit *u) { + Iterator i; + Unit *other; + + assert(u); + assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); + + SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i) + if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); +} + +void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { + assert(u); + assert(os < _UNIT_ACTIVE_STATE_MAX); + assert(ns < _UNIT_ACTIVE_STATE_MAX); + assert(!(os == UNIT_ACTIVE && ns == UNIT_ACTIVATING)); + assert(!(os == UNIT_INACTIVE && ns == UNIT_DEACTIVATING)); + + if (os == ns) + return; + + if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) + u->meta.active_enter_timestamp = now(CLOCK_REALTIME); + else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) + u->meta.active_exit_timestamp = now(CLOCK_REALTIME); + + if (u->meta.job) { + + if (u->meta.job->state == JOB_WAITING) + + /* 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_schedule_run(u->meta.job); + + else { + assert(u->meta.job->state == JOB_RUNNING); + + /* Let's check of this state change + * constitutes a finished job, or maybe + * cotradicts a running job and hence needs to + * invalidate jobs. */ + + switch (u->meta.job->type) { + + case JOB_START: + case JOB_VERIFY_ACTIVE: + + if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) { + job_finish_and_invalidate(u->meta.job, true); + return; + } else if (ns == UNIT_ACTIVATING) + return; + else + job_finish_and_invalidate(u->meta.job, false); + + break; + + case JOB_RELOAD: + case JOB_RELOAD_OR_START: + + if (ns == UNIT_ACTIVE) { + job_finish_and_invalidate(u->meta.job, true); + return; + } else if (ns == UNIT_ACTIVATING || ns == UNIT_ACTIVE_RELOADING) + return; + else + job_finish_and_invalidate(u->meta.job, false); + + break; + + case JOB_STOP: + case JOB_RESTART: + case JOB_TRY_RESTART: + + if (ns == UNIT_INACTIVE) { + job_finish_and_invalidate(u->meta.job, true); + return; + } else if (ns == UNIT_DEACTIVATING) + return; + else + job_finish_and_invalidate(u->meta.job, false); + + break; + + default: + assert_not_reached("Job type unknown"); + } + } + } + + /* If this state change happened without being requested by a + * job, then let's retroactively start or stop dependencies */ + + if (UNIT_IS_INACTIVE_OR_DEACTIVATING(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); +} + +int unit_watch_fd(Unit *u, int fd, uint32_t events) { + struct epoll_event ev; + + assert(u); + assert(fd >= 0); + + zero(ev); + ev.data.fd = fd; + ev.data.ptr = u; + ev.data.u32 = MANAGER_FD; + ev.events = events; + + if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) >= 0) + return 0; + + if (errno == EEXIST) + if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_MOD, fd, &ev) >= 0) + return 0; + + return -errno; +} + +void unit_unwatch_fd(Unit *u, int fd) { + assert(u); + assert(fd >= 0); + + assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, fd, NULL) >= 0 || errno == ENOENT); +} + +int unit_watch_pid(Unit *u, pid_t pid) { + assert(u); + assert(pid >= 1); + + return hashmap_put(u->meta.manager->watch_pids, UINT32_TO_PTR(pid), u); +} + +void unit_unwatch_pid(Unit *u, pid_t pid) { + assert(u); + assert(pid >= 1); + + hashmap_remove(u->meta.manager->watch_pids, UINT32_TO_PTR(pid)); +} + +int unit_watch_timer(Unit *u, usec_t delay, int *id) { + struct epoll_event ev; + int fd; + struct itimerspec its; + int flags; + bool ours; + + assert(u); + assert(id); + + /* This will try to reuse the old timer if there is one */ + + if (*id >= 0) { + ours = false; + fd = *id; + + } else { + ours = true; + + if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) + return -errno; + } + + 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; + + zero(ev); + ev.data.fd = fd; + ev.data.ptr = u; + ev.data.u32 = MANAGER_TIMER; + ev.events = POLLIN; + + if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) + goto fail; + + *id = fd; + return 0; + +fail: + if (ours) + assert_se(close_nointr(fd) == 0); + + return -errno; +} + +void unit_unwatch_timer(Unit *u, int *id) { + assert(u); + assert(id); + + if (*id < 0) + return; + + assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, *id, NULL) >= 0); + assert_se(close_nointr(*id) == 0); + *id = -1; +} + +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: + return true; + + case JOB_STOP: + 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) { + + static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = { + [UNIT_REQUIRES] = UNIT_REQUIRED_BY, + [UNIT_SOFT_REQUIRES] = UNIT_SOFT_REQUIRED_BY, + [UNIT_WANTS] = UNIT_WANTED_BY, + [UNIT_REQUISITE] = UNIT_REQUIRED_BY, + [UNIT_SOFT_REQUISITE] = UNIT_SOFT_REQUIRED_BY, + [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID, + [UNIT_SOFT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID, + [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID, + [UNIT_CONFLICTS] = UNIT_CONFLICTS, + [UNIT_BEFORE] = UNIT_AFTER, + [UNIT_AFTER] = UNIT_BEFORE + }; + int r; + + assert(u); + assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX); + assert(inverse_table[d] != _UNIT_DEPENDENCY_INVALID); + assert(other); + + /* 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->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) + return r; + + if ((r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) + return r; + + if ((r = set_put(u->meta.dependencies[d], other)) < 0) + return r; + + if ((r = set_put(other->meta.dependencies[inverse_table[d]], u)) < 0) { + set_remove(u->meta.dependencies[d], other); + return r; + } + + return 0; +} diff --git a/unit.h b/unit.h new file mode 100644 index 00000000..7803f318 --- /dev/null +++ b/unit.h @@ -0,0 +1,235 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foounithfoo +#define foounithfoo + +#include +#include + +typedef union Unit Unit; +typedef struct Meta Meta; +typedef struct UnitVTable UnitVTable; +typedef enum UnitType UnitType; +typedef enum UnitLoadState UnitLoadState; +typedef enum UnitActiveState UnitActiveState; +typedef enum UnitDependency UnitDependency; + +#include "job.h" +#include "manager.h" +#include "set.h" +#include "util.h" +#include "list.h" +#include "socket-util.h" +#include "execute.h" +#include "util.h" + +#define UNIT_NAME_MAX 32 +#define DEFAULT_TIMEOUT_USEC (20*USEC_PER_SEC) +#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) + +enum UnitType { + UNIT_SERVICE = 0, + UNIT_TIMER, + UNIT_SOCKET, + UNIT_TARGET, + UNIT_DEVICE, + UNIT_MOUNT, + UNIT_AUTOMOUNT, + UNIT_SNAPSHOT, + _UNIT_TYPE_MAX, + _UNIT_TYPE_INVALID = -1, +}; + +enum UnitLoadState { + UNIT_STUB, + UNIT_LOADED, + UNIT_FAILED, + _UNIT_LOAD_STATE_MAX +}; + +enum UnitActiveState { + UNIT_ACTIVE, + UNIT_ACTIVE_RELOADING, + UNIT_INACTIVE, + UNIT_ACTIVATING, + UNIT_DEACTIVATING, + _UNIT_ACTIVE_STATE_MAX +}; + +static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) { + return t == UNIT_ACTIVE || t == UNIT_ACTIVE_RELOADING; +} + +static inline bool UNIT_IS_ACTIVE_OR_ACTIVATING(UnitActiveState t) { + return t == UNIT_ACTIVE || t == UNIT_ACTIVATING || t == UNIT_ACTIVE_RELOADING; +} + +static inline bool UNIT_IS_INACTIVE_OR_DEACTIVATING(UnitActiveState t) { + return t == UNIT_INACTIVE || t == UNIT_DEACTIVATING; +} + +enum UnitDependency { + /* Positive dependencies */ + UNIT_REQUIRES, + UNIT_SOFT_REQUIRES, + UNIT_WANTS, + UNIT_REQUISITE, + UNIT_SOFT_REQUISITE, + + /* Inverse of the above */ + UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */ + UNIT_SOFT_REQUIRED_BY, /* inverse of 'soft_requires' and 'soft_requisite' is 'soft_required_by' */ + UNIT_WANTED_BY, /* inverse of 'wants' */ + + /* Negative dependencies */ + UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicts' */ + + /* Order */ + UNIT_BEFORE, /* inverse of before is after and vice versa */ + UNIT_AFTER, + + _UNIT_DEPENDENCY_MAX, + _UNIT_DEPENDENCY_INVALID = -1 +}; + +struct Meta { + Manager *manager; + UnitType type; + UnitLoadState load_state; + + char *id; /* One name is special because we use it for identification. Points to an entry in the names set */ + + Set *names; + Set *dependencies[_UNIT_DEPENDENCY_MAX]; + + char *description; + + /* If there is something to do with this unit, then this is + * the job for it */ + Job *job; + + bool in_load_queue:1; + + usec_t active_enter_timestamp; + usec_t active_exit_timestamp; + + /* Load queue */ + LIST_FIELDS(Meta, load_queue); +}; + +#include "service.h" +#include "timer.h" +#include "socket.h" +#include "target.h" +#include "device.h" +#include "mount.h" +#include "automount.h" +#include "snapshot.h" + +union Unit { + Meta meta; + Service service; + Timer timer; + Socket socket; + Target target; + Device device; + Mount mount; + Automount automount; + Snapshot snapshot; +}; + +struct UnitVTable { + const char *suffix; + + int (*init)(Unit *u); + void (*done)(Unit *u); + + void (*dump)(Unit *u, FILE *f, const char *prefix); + + int (*start)(Unit *u); + int (*stop)(Unit *u); + int (*reload)(Unit *u); + + bool (*can_reload)(Unit *u); + + /* Boils down the more complex internal state of this unit to + * a simpler one that the engine can understand */ + UnitActiveState (*active_state)(Unit *u); + + void (*fd_event)(Unit *u, int fd, uint32_t events); + void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); + void (*timer_event)(Unit *u, int id, uint64_t n_elapsed); + + void (*retry)(Unit *u); +}; + +extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; + +#define UNIT_VTABLE(u) unit_vtable[(u)->meta.type] + +/* For casting a unit into the various unit types */ +#define DEFINE_CAST(UPPERCASE, MixedCase) \ + static inline MixedCase* UPPERCASE(Unit *u) { \ + if (!u || u->meta.type != UNIT_##UPPERCASE) \ + return NULL; \ + \ + return (MixedCase*) u; \ + } + +/* For casting the various unit types into a unit */ +#define UNIT(u) ((Unit*) (u)) + +DEFINE_CAST(SOCKET, Socket); +DEFINE_CAST(TIMER, Timer); +DEFINE_CAST(SERVICE, Service); +DEFINE_CAST(TARGET, Target); +DEFINE_CAST(DEVICE, Device); +DEFINE_CAST(MOUNT, Mount); +DEFINE_CAST(AUTOMOUNT, Automount); +DEFINE_CAST(SNAPSHOT, Snapshot); + +UnitType unit_name_to_type(const char *n); +bool unit_name_is_valid(const char *n); +char *unit_name_change_suffix(const char *n, const char *suffix); + +Unit *unit_new(Manager *m); +void unit_free(Unit *u); + +int unit_add_name(Unit *u, const char *name); +int unit_add_dependency(Unit *u, UnitDependency d, Unit *other); + +void unit_add_to_load_queue(Unit *u); + +int unit_merge(Unit *u, Unit *other); + +int unit_load_fragment_and_dropin(Unit *u); +int unit_load(Unit *unit); + +const char* unit_id(Unit *u); +const char *unit_description(Unit *u); + +UnitActiveState unit_active_state(Unit *u); + +void unit_dump(Unit *u, FILE *f, const char *prefix); + +bool unit_can_reload(Unit *u); +bool unit_can_start(Unit *u); + +int unit_start(Unit *u); +int unit_stop(Unit *u); +int unit_reload(Unit *u); + +void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns); + +int unit_watch_fd(Unit *u, int fd, uint32_t events); +void unit_unwatch_fd(Unit *u, int fd); + +int unit_watch_pid(Unit *u, pid_t pid); +void unit_unwatch_pid(Unit *u, pid_t pid); + +int unit_watch_timer(Unit *u, usec_t delay, int *id); +void unit_unwatch_timer(Unit *u, int *id); + +bool unit_job_is_applicable(Unit *u, JobType j); + +#endif diff --git a/util.c b/util.c index b4b07e9d..f752a248 100644 --- a/util.c +++ b/util.c @@ -402,3 +402,44 @@ char *strappend(const char *s, const char *suffix) { return r; } + +int readlink_malloc(const char *p, char **r) { + size_t l = 100; + + assert(p); + assert(r); + + for (;;) { + char *c; + ssize_t n; + + if (!(c = new(char, l))) + return -ENOMEM; + + if ((n = readlink(p, c, l-1)) < 0) { + int ret = -errno; + free(c); + return ret; + } + + if ((size_t) n < l-1) { + c[n] = 0; + *r = c; + return 0; + } + + free(c); + l *= 2; + } +} + +char *file_name_from_path(const char *p) { + char *r; + + assert(p); + + if ((r = strrchr(p, '/'))) + return r + 1; + + return (char*) p; +} diff --git a/util.h b/util.h index 3e86b298..8cc6043f 100644 --- a/util.h +++ b/util.h @@ -91,4 +91,8 @@ int read_one_line_file(const char *fn, char **line); char *strappend(const char *s, const char *suffix); +int readlink_malloc(const char *p, char **r); + +char *file_name_from_path(const char *p); + #endif