LIBS=-lrt -lcap
COMMON= \
- name.o \
+ unit.o \
util.o \
set.o \
hashmap.o \
#include <errno.h>
-#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",
};
AutomountExecCommand c;
- Automount *s = AUTOMOUNT(n);
+ Automount *s = AUTOMOUNT(u);
assert(s);
}
}
-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,
typedef struct Automount Automount;
-#include "name.h"
+#include "unit.h"
typedef enum AutomountState {
AUTOMOUNT_DEAD,
Mount *mount;
};
-extern const NameVTable automount_vtable;
+extern const UnitVTable automount_vtable;
#endif
}
}
- r = config_parse(fn, sections, t, userdata);
+ r = config_parse(fn, NULL, sections, t, userdata);
free(path);
return r;
}
}
/* 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)) {
/* 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);
/*-*- 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);
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,
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 */
char **sysfs;
};
-extern const NameVTable device_vtable;
+extern const UnitVTable device_vtable;
#endif
- 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
- 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
#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;
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 */
/* 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;
"%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));
}
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
* . */
* 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;
}
/* 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 ||
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;
}
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;
}
}
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);
return 0;
}
- n = j->name;
+ u = j->unit;
t = j->type;
job_free(j);
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 ||
} 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 ||
}
/* 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);
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,
Manager *manager;
uint32_t id;
- Name *name;
+ Unit *unit;
JobType type;
JobState state;
};
-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);
#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 */
#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
/*-*- Mode: C; c-basic-offset: 8 -*-*/
+#include <linux/oom.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
-#include <linux/oom.h>
+#include <unistd.h>
+#include <fcntl.h>
-#include "name.h"
+#include "unit.h"
#include "strv.h"
#include "conf-parser.h"
#include "load-fragment.h"
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;
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;
}
void *data,
void *userdata) {
- Set **set = data;
- Name *name = userdata;
+ Unit *u = userdata;
char *w;
size_t l;
char *state;
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);
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) \
{ "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;
}
#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
#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 */
#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
int main(int argc, char *argv[]) {
Manager *m = NULL;
- Name *target = NULL;
+ Unit *target = NULL;
Job *job = NULL;
int r, retval = 1;
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;
}
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");
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)))
}
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);
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);
}
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. */
if (!l->matters)
continue;
- /* This name has already been marked */
+ /* This unit has already been marked */
if (l->object->generation == generation)
continue;
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. */
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;
}
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) {
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)
static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation) {
Iterator i;
- Name *n;
+ Unit *u;
int r;
assert(m);
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;
}
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)
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;
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;
}
/* 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;
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
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)
}
}
- 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;
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;
}
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;
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. */
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;
}
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;
}
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) {
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);
/* 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;
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) {
for (;;) {
siginfo_t si;
- Name *n;
+ Unit *u;
zero(si);
if (waitid(P_ALL, 0, &si, WNOHANG) < 0)
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;
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;
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;
}
typedef struct Manager Manager;
typedef enum ManagerEventType ManagerEventType;
-#include "name.h"
+#include "unit.h"
#include "job.h"
#include "hashmap.h"
#include "list.h"
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;
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);
#include <errno.h>
-#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",
[MOUNT_MAINTAINANCE] = "maintainance"
};
- Mount *s = MOUNT(n);
+ Mount *s = MOUNT(u);
assert(s);
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,
typedef struct Mount Mount;
-#include "name.h"
+#include "unit.h"
typedef enum MountState {
MOUNT_DEAD,
char *path;
};
-extern const NameVTable mount_vtable;
+extern const UnitVTable mount_vtable;
#endif
+++ /dev/null
-/*-*- Mode: C; c-basic-offset: 8 -*-*/
-
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <sys/poll.h>
-
-#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;
-}
+++ /dev/null
-/*-*- Mode: C; c-basic-offset: 8 -*-*/
-
-#ifndef foonamehfoo
-#define foonamehfoo
-
-#include <stdbool.h>
-#include <stdlib.h>
-
-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
#include <errno.h>
#include <signal.h>
-#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);
/* 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) {
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);
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",
};
ServiceExecCommand c;
- Service *s = SERVICE(n);
+ Service *s = SERVICE(u);
char *prefix2;
assert(s);
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 &&
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;
}
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;
}
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) {
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)
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;
free(fds);
if (timeout)
- name_unwatch_timer(NAME(s), &s->timer_id);
+ unit_unwatch_timer(UNIT(s), &s->timer_id);
return r;
}
(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);
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);
}
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);
}
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;
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
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);
return 0;
}
-static int service_stop(Name *n) {
- Service *s = SERVICE(n);
+static int service_stop(Unit *u) {
+ Service *s = SERVICE(u);
assert(s);
return 0;
}
-static int service_reload(Name *n) {
- Service *s = SERVICE(n);
+static int service_reload(Unit *u) {
+ Service *s = SERVICE(u);
assert(s);
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) {
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);
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. */
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. */
* 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 */
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);
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;
* 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;
}
}
-const NameVTable service_vtable = {
+const UnitVTable service_vtable = {
.suffix = ".service",
.init = service_init,
typedef struct Service Service;
-#include "name.h"
+#include "unit.h"
typedef enum ServiceState {
SERVICE_DEAD,
int timer_id;
};
-const NameVTable service_vtable;
+const UnitVTable service_vtable;
#endif
/*-*- 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,
typedef struct Snapshot Snapshot;
-#include "name.h"
+#include "unit.h"
typedef enum SnapshotState {
SNAPSHOT_DEAD,
bool cleanup:1;
};
-extern const NameVTable snapshot_vtable;
+extern const UnitVTable snapshot_vtable;
#endif
#include <sys/poll.h>
#include <signal.h>
-#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);
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;
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;
}
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",
};
SocketExecCommand c;
- Socket *s = SOCKET(n);
+ Socket *s = SOCKET(u);
SocketPort *p;
assert(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;
if (p->fd < 0)
continue;
- name_unwatch_fd(NAME(s), p->fd);
+ unit_unwatch_fd(UNIT(s), p->fd);
}
}
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;
}
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 &&
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;
}
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) {
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;
fail:
if (timeout)
- name_unwatch_timer(NAME(s), &s->timer_id);
+ unit_unwatch_timer(UNIT(s), &s->timer_id);
return r;
}
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);
}
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);
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);
}
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;
}
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);
}
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);
}
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);
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);
return 0;
}
-static int socket_stop(Name *n) {
- Socket *s = SOCKET(n);
+static int socket_stop(Unit *u) {
+ Socket *s = SOCKET(u);
assert(s);
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);
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);
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)))
}
}
-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);
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;
return 0;
}
-const NameVTable socket_vtable = {
+const UnitVTable socket_vtable = {
.suffix = ".socket",
.init = socket_init,
typedef struct Socket Socket;
-#include "name.h"
+#include "unit.h"
#include "socket-util.h"
typedef enum SocketState {
/* 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
/*-*- 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
typedef struct Target Target;
-#include "name.h"
+#include "unit.h"
typedef enum TargetState {
TARGET_DEAD,
TargetState state;
};
-extern const NameVTable target_vtable;
+extern const UnitVTable target_vtable;
#endif
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);
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);
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);
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);
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);
/*-*- 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
typedef struct Timer Timer;
-#include "name.h"
+#include "unit.h"
typedef enum TimerState {
TIMER_DEAD,
Service *service;
};
-const NameVTable timer_vtable;
+const UnitVTable timer_vtable;
#endif
--- /dev/null
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/poll.h>
+
+#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;
+}
--- /dev/null
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foounithfoo
+#define foounithfoo
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+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
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;
+}
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