mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
s/name/unit
This commit is contained in:
parent
fd79db6a5d
commit
87f0e418cf
2
Makefile
2
Makefile
@ -2,7 +2,7 @@ CFLAGS=-Wall -Wextra -O0 -g -pipe -D_GNU_SOURCE -fdiagnostics-show-option -Wno-u
|
||||
LIBS=-lrt -lcap
|
||||
|
||||
COMMON= \
|
||||
name.o \
|
||||
unit.o \
|
||||
util.o \
|
||||
set.o \
|
||||
hashmap.o \
|
||||
|
44
automount.c
44
automount.c
@ -2,43 +2,43 @@
|
||||
|
||||
#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",
|
||||
@ -59,7 +59,7 @@ static void automount_dump(Name *n, FILE *f, const char *prefix) {
|
||||
};
|
||||
|
||||
AutomountExecCommand c;
|
||||
Automount *s = AUTOMOUNT(n);
|
||||
Automount *s = AUTOMOUNT(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -79,23 +79,23 @@ static void automount_dump(Name *n, FILE *f, const char *prefix) {
|
||||
}
|
||||
}
|
||||
|
||||
static NameActiveState automount_active_state(Name *n) {
|
||||
static UnitActiveState automount_active_state(Unit *u) {
|
||||
|
||||
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 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,
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
typedef struct Automount Automount;
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum AutomountState {
|
||||
AUTOMOUNT_DEAD,
|
||||
@ -41,6 +41,6 @@ struct Automount {
|
||||
Mount *mount;
|
||||
};
|
||||
|
||||
extern const NameVTable automount_vtable;
|
||||
extern const UnitVTable automount_vtable;
|
||||
|
||||
#endif
|
||||
|
@ -109,7 +109,7 @@ static int parse_line(const char *filename, unsigned line, char **section, const
|
||||
}
|
||||
}
|
||||
|
||||
r = config_parse(fn, sections, t, userdata);
|
||||
r = config_parse(fn, NULL, sections, t, userdata);
|
||||
free(path);
|
||||
return r;
|
||||
}
|
||||
@ -162,19 +162,20 @@ static int parse_line(const char *filename, unsigned line, char **section, const
|
||||
}
|
||||
|
||||
/* Go through the file and parse each line */
|
||||
int config_parse(const char *filename, const char* const * sections, const ConfigItem *t, void *userdata) {
|
||||
int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) {
|
||||
unsigned line = 0;
|
||||
char *section = NULL;
|
||||
FILE *f;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(t);
|
||||
|
||||
if (!(f = fopen(filename, "re"))) {
|
||||
r = -errno;
|
||||
log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
|
||||
goto finish;
|
||||
if (!f) {
|
||||
if (!(f = fopen(filename, "re"))) {
|
||||
r = -errno;
|
||||
log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
while (!feof(f)) {
|
||||
|
@ -21,7 +21,7 @@ typedef struct ConfigItem {
|
||||
/* The configuration file parsing routine. Expects a table of
|
||||
* config_items in *t that is terminated by an item where lvalue is
|
||||
* NULL */
|
||||
int config_parse(const char *filename, const char* const * sections, const ConfigItem *t, void *userdata);
|
||||
int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata);
|
||||
|
||||
/* Generic parsers */
|
||||
int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
|
18
device.c
18
device.c
@ -1,24 +1,24 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
#include "device.h"
|
||||
#include "strv.h"
|
||||
|
||||
static void device_done(Name *n) {
|
||||
Device *d = DEVICE(n);
|
||||
static void device_done(Unit *u) {
|
||||
Device *d = DEVICE(u);
|
||||
|
||||
assert(d);
|
||||
strv_free(d->sysfs);
|
||||
}
|
||||
|
||||
static void device_dump(Name *n, FILE *f, const char *prefix) {
|
||||
static void device_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
static const char* const state_table[_DEVICE_STATE_MAX] = {
|
||||
[DEVICE_DEAD] = "dead",
|
||||
[DEVICE_AVAILABLE] = "available"
|
||||
};
|
||||
|
||||
Device *s = DEVICE(n);
|
||||
Device *s = DEVICE(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -27,14 +27,14 @@ static void device_dump(Name *n, FILE *f, const char *prefix) {
|
||||
prefix, state_table[s->state]);
|
||||
}
|
||||
|
||||
static NameActiveState device_active_state(Name *n) {
|
||||
return DEVICE(n)->state == DEVICE_DEAD ? NAME_INACTIVE : NAME_ACTIVE;
|
||||
static UnitActiveState device_active_state(Unit *u) {
|
||||
return DEVICE(u)->state == DEVICE_DEAD ? UNIT_INACTIVE : UNIT_ACTIVE;
|
||||
}
|
||||
|
||||
const NameVTable device_vtable = {
|
||||
const UnitVTable device_vtable = {
|
||||
.suffix = ".device",
|
||||
|
||||
.init = name_load_fragment_and_dropin,
|
||||
.init = unit_load_fragment_and_dropin,
|
||||
.done = device_done,
|
||||
.dump = device_dump,
|
||||
|
||||
|
4
device.h
4
device.h
@ -5,7 +5,7 @@
|
||||
|
||||
typedef struct Device Device;
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
/* We simply watch devices, we cannot plug/unplug them. That
|
||||
* simplifies the state engine greatly */
|
||||
@ -24,6 +24,6 @@ struct Device {
|
||||
char **sysfs;
|
||||
};
|
||||
|
||||
extern const NameVTable device_vtable;
|
||||
extern const UnitVTable device_vtable;
|
||||
|
||||
#endif
|
||||
|
6
fixme
6
fixme
@ -3,9 +3,9 @@
|
||||
|
||||
- need gc for active jobs that nothing cares for
|
||||
|
||||
- need gc for names that are not referenced anymore
|
||||
- need gc for units that are not referenced anymore
|
||||
|
||||
- refreshing of names (i.e. reload config files)
|
||||
- refreshing of units (i.e. reload config files)
|
||||
|
||||
- dbusification
|
||||
|
||||
@ -37,6 +37,6 @@
|
||||
|
||||
- templating/instances
|
||||
|
||||
- verify fragment data after loading: refuse cycles on yourself, service names contradicting, more than one Start executable, ...
|
||||
- verify fragment data after loading: refuse cycles on yourself, service units contradicting, more than one Start executable, ...
|
||||
|
||||
- rate limit startups
|
||||
|
74
job.c
74
job.c
@ -6,12 +6,12 @@
|
||||
#include "macro.h"
|
||||
#include "job.h"
|
||||
|
||||
Job* job_new(Manager *m, JobType type, Name *name) {
|
||||
Job* job_new(Manager *m, JobType type, Unit *unit) {
|
||||
Job *j;
|
||||
|
||||
assert(m);
|
||||
assert(type < _JOB_TYPE_MAX);
|
||||
assert(name);
|
||||
assert(unit);
|
||||
|
||||
if (!(j = new0(Job, 1)))
|
||||
return NULL;
|
||||
@ -19,7 +19,7 @@ Job* job_new(Manager *m, JobType type, Name *name) {
|
||||
j->manager = m;
|
||||
j->id = m->current_job_id++;
|
||||
j->type = type;
|
||||
j->name = name;
|
||||
j->unit = unit;
|
||||
|
||||
/* We don't link it here, that's what job_dependency() is for */
|
||||
|
||||
@ -31,8 +31,8 @@ void job_free(Job *j) {
|
||||
|
||||
/* Detach from next 'bigger' objects */
|
||||
if (j->installed) {
|
||||
if (j->name->meta.job == j)
|
||||
j->name->meta.job = NULL;
|
||||
if (j->unit->meta.job == j)
|
||||
j->unit->meta.job = NULL;
|
||||
|
||||
hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
|
||||
j->installed = false;
|
||||
@ -142,7 +142,7 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
|
||||
"%s\tState: %s\n"
|
||||
"%s\tForced: %s\n",
|
||||
prefix, j->id,
|
||||
prefix, name_id(j->name), job_type_to_string(j->type),
|
||||
prefix, unit_id(j->unit), job_type_to_string(j->type),
|
||||
prefix, job_state_table[j->state],
|
||||
prefix, yes_no(j->forced));
|
||||
}
|
||||
@ -257,12 +257,12 @@ bool job_type_is_conflicting(JobType a, JobType b) {
|
||||
|
||||
bool job_is_runnable(Job *j) {
|
||||
Iterator i;
|
||||
Name *other;
|
||||
Unit *other;
|
||||
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
|
||||
/* Checks whether there is any job running for the names this
|
||||
/* Checks whether there is any job running for the units this
|
||||
* job needs to be running after (in the case of a 'positive'
|
||||
* job type) or before (in the case of a 'negative' job type
|
||||
* . */
|
||||
@ -277,7 +277,7 @@ bool job_is_runnable(Job *j) {
|
||||
* dependencies, regardless whether they are
|
||||
* starting or stopping something. */
|
||||
|
||||
SET_FOREACH(other, j->name->meta.dependencies[NAME_AFTER], i)
|
||||
SET_FOREACH(other, j->unit->meta.dependencies[UNIT_AFTER], i)
|
||||
if (other->meta.job)
|
||||
return false;
|
||||
}
|
||||
@ -285,7 +285,7 @@ bool job_is_runnable(Job *j) {
|
||||
/* Also, if something else is being stopped and we should
|
||||
* change state after it, then lets wait. */
|
||||
|
||||
SET_FOREACH(other, j->name->meta.dependencies[NAME_BEFORE], i)
|
||||
SET_FOREACH(other, j->unit->meta.dependencies[UNIT_BEFORE], i)
|
||||
if (other->meta.job &&
|
||||
(other->meta.job->type == JOB_STOP ||
|
||||
other->meta.job->type == JOB_RESTART ||
|
||||
@ -328,16 +328,16 @@ int job_run_and_invalidate(Job *j) {
|
||||
switch (j->type) {
|
||||
|
||||
case JOB_START:
|
||||
r = name_start(j->name);
|
||||
r = unit_start(j->unit);
|
||||
if (r == -EBADR)
|
||||
r = 0;
|
||||
break;
|
||||
|
||||
case JOB_VERIFY_ACTIVE: {
|
||||
NameActiveState t = name_active_state(j->name);
|
||||
if (NAME_IS_ACTIVE_OR_RELOADING(t))
|
||||
UnitActiveState t = unit_active_state(j->unit);
|
||||
if (UNIT_IS_ACTIVE_OR_RELOADING(t))
|
||||
r = -EALREADY;
|
||||
else if (t == NAME_ACTIVATING)
|
||||
else if (t == UNIT_ACTIVATING)
|
||||
r = -EAGAIN;
|
||||
else
|
||||
r = -ENOEXEC;
|
||||
@ -345,39 +345,39 @@ int job_run_and_invalidate(Job *j) {
|
||||
}
|
||||
|
||||
case JOB_STOP:
|
||||
r = name_stop(j->name);
|
||||
r = unit_stop(j->unit);
|
||||
break;
|
||||
|
||||
case JOB_RELOAD:
|
||||
r = name_reload(j->name);
|
||||
r = unit_reload(j->unit);
|
||||
break;
|
||||
|
||||
case JOB_RELOAD_OR_START:
|
||||
if (name_active_state(j->name) == NAME_ACTIVE)
|
||||
r = name_reload(j->name);
|
||||
if (unit_active_state(j->unit) == UNIT_ACTIVE)
|
||||
r = unit_reload(j->unit);
|
||||
else
|
||||
r = name_start(j->name);
|
||||
r = unit_start(j->unit);
|
||||
break;
|
||||
|
||||
case JOB_RESTART: {
|
||||
NameActiveState t = name_active_state(j->name);
|
||||
if (t == NAME_INACTIVE || t == NAME_ACTIVATING) {
|
||||
UnitActiveState t = unit_active_state(j->unit);
|
||||
if (t == UNIT_INACTIVE || t == UNIT_ACTIVATING) {
|
||||
j->type = JOB_START;
|
||||
r = name_start(j->name);
|
||||
r = unit_start(j->unit);
|
||||
} else
|
||||
r = name_stop(j->name);
|
||||
r = unit_stop(j->unit);
|
||||
break;
|
||||
}
|
||||
|
||||
case JOB_TRY_RESTART: {
|
||||
NameActiveState t = name_active_state(j->name);
|
||||
if (t == NAME_INACTIVE || t == NAME_DEACTIVATING)
|
||||
UnitActiveState t = unit_active_state(j->unit);
|
||||
if (t == UNIT_INACTIVE || t == UNIT_DEACTIVATING)
|
||||
r = -ENOEXEC;
|
||||
else if (t == NAME_ACTIVATING) {
|
||||
else if (t == UNIT_ACTIVATING) {
|
||||
j->type = JOB_START;
|
||||
r = name_start(j->name);
|
||||
r = unit_start(j->unit);
|
||||
} else
|
||||
r = name_stop(j->name);
|
||||
r = unit_stop(j->unit);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -397,9 +397,9 @@ int job_run_and_invalidate(Job *j) {
|
||||
}
|
||||
|
||||
int job_finish_and_invalidate(Job *j, bool success) {
|
||||
Name *n;
|
||||
Name *other;
|
||||
NameType t;
|
||||
Unit *u;
|
||||
Unit *other;
|
||||
UnitType t;
|
||||
Iterator i;
|
||||
|
||||
assert(j);
|
||||
@ -413,7 +413,7 @@ int job_finish_and_invalidate(Job *j, bool success) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = j->name;
|
||||
u = j->unit;
|
||||
t = j->type;
|
||||
job_free(j);
|
||||
|
||||
@ -424,14 +424,14 @@ int job_finish_and_invalidate(Job *j, bool success) {
|
||||
t == JOB_VERIFY_ACTIVE ||
|
||||
t == JOB_RELOAD_OR_START) {
|
||||
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRED_BY], i)
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i)
|
||||
if (other->meta.job &&
|
||||
(other->meta.type == JOB_START ||
|
||||
other->meta.type == JOB_VERIFY_ACTIVE ||
|
||||
other->meta.type == JOB_RELOAD_OR_START))
|
||||
job_finish_and_invalidate(other->meta.job, false);
|
||||
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRED_BY], i)
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRED_BY], i)
|
||||
if (other->meta.job &&
|
||||
!other->meta.job->forced &&
|
||||
(other->meta.type == JOB_START ||
|
||||
@ -441,7 +441,7 @@ int job_finish_and_invalidate(Job *j, bool success) {
|
||||
|
||||
} else if (t == JOB_STOP) {
|
||||
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], i)
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTS], i)
|
||||
if (other->meta.job &&
|
||||
(t == JOB_START ||
|
||||
t == JOB_VERIFY_ACTIVE ||
|
||||
@ -451,10 +451,10 @@ int job_finish_and_invalidate(Job *j, bool success) {
|
||||
}
|
||||
|
||||
/* Try to start the next jobs that can be started */
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], i)
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_AFTER], i)
|
||||
if (other->meta.job)
|
||||
job_schedule_run(other->meta.job);
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], i)
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_BEFORE], i)
|
||||
if (other->meta.job)
|
||||
job_schedule_run(other->meta.job);
|
||||
|
||||
|
8
job.h
8
job.h
@ -13,12 +13,12 @@ typedef enum JobState JobState;
|
||||
typedef enum JobMode JobMode;
|
||||
|
||||
#include "manager.h"
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
#include "hashmap.h"
|
||||
#include "list.h"
|
||||
|
||||
enum JobType {
|
||||
JOB_START, /* if a name does not support being started, we'll just wait until it becomes active */
|
||||
JOB_START, /* if a unit does not support being started, we'll just wait until it becomes active */
|
||||
JOB_VERIFY_ACTIVE,
|
||||
|
||||
JOB_STOP,
|
||||
@ -64,7 +64,7 @@ struct Job {
|
||||
Manager *manager;
|
||||
uint32_t id;
|
||||
|
||||
Name *name;
|
||||
Unit *unit;
|
||||
|
||||
JobType type;
|
||||
JobState state;
|
||||
@ -86,7 +86,7 @@ struct Job {
|
||||
|
||||
};
|
||||
|
||||
Job* job_new(Manager *m, JobType type, Name *name);
|
||||
Job* job_new(Manager *m, JobType type, Unit *unit);
|
||||
void job_free(Job *job);
|
||||
void job_dump(Job *j, FILE*f, const char *prefix);
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
#include "load-dropin.h"
|
||||
|
||||
int name_load_dropin(Name *n) {
|
||||
assert(n);
|
||||
int unit_load_dropin(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
/* Load dependencies from supplementary drop-in directories */
|
||||
|
||||
|
@ -3,10 +3,10 @@
|
||||
#ifndef fooloaddropinhfoo
|
||||
#define fooloaddropinhfoo
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
/* Read service data supplementary drop-in directories */
|
||||
|
||||
int name_load_dropin(Name *n);
|
||||
int unit_load_dropin(Unit *u);
|
||||
|
||||
#endif
|
||||
|
301
load-fragment.c
301
load-fragment.c
@ -1,11 +1,13 @@
|
||||
/*-*- 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"
|
||||
@ -20,8 +22,8 @@ static int config_parse_deps(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
NameDependency d = PTR_TO_UINT(data);
|
||||
Name *name = userdata;
|
||||
UnitDependency d = PTR_TO_UINT(data);
|
||||
Unit *u = userdata;
|
||||
char *w;
|
||||
size_t l;
|
||||
char *state;
|
||||
@ -33,18 +35,18 @@ static int config_parse_deps(
|
||||
FOREACH_WORD(w, &l, rvalue, state) {
|
||||
char *t;
|
||||
int r;
|
||||
Name *other;
|
||||
Unit *other;
|
||||
|
||||
if (!(t = strndup(w, l)))
|
||||
return -ENOMEM;
|
||||
|
||||
r = manager_load_name(name->meta.manager, t, &other);
|
||||
r = manager_load_unit(u->meta.manager, t, &other);
|
||||
free(t);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if ((r = name_add_dependency(name, d, other)) < 0)
|
||||
if ((r = unit_add_dependency(u, d, other)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -60,8 +62,7 @@ static int config_parse_names(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Set **set = data;
|
||||
Name *name = userdata;
|
||||
Unit *u = userdata;
|
||||
char *w;
|
||||
size_t l;
|
||||
char *state;
|
||||
@ -74,42 +75,33 @@ static int config_parse_names(
|
||||
FOREACH_WORD(w, &l, rvalue, state) {
|
||||
char *t;
|
||||
int r;
|
||||
Name *other;
|
||||
Unit *other;
|
||||
|
||||
if (!(t = strndup(w, l)))
|
||||
return -ENOMEM;
|
||||
|
||||
other = manager_get_name(name->meta.manager, t);
|
||||
other = manager_get_unit(u->meta.manager, t);
|
||||
|
||||
if (other) {
|
||||
|
||||
if (other != name) {
|
||||
if (other != u) {
|
||||
|
||||
if (other->meta.load_state != NAME_STUB) {
|
||||
if (other->meta.load_state != UNIT_STUB) {
|
||||
free(t);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if ((r = name_merge(name, other)) < 0) {
|
||||
if ((r = unit_merge(u, other)) < 0) {
|
||||
free(t);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!*set)
|
||||
if (!(*set = set_new(trivial_hash_func, trivial_compare_func))) {
|
||||
free(t);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if ((r = set_put(*set, t)) < 0) {
|
||||
if ((r = unit_add_name(u, t)) < 0) {
|
||||
free(t);
|
||||
return r;
|
||||
}
|
||||
|
||||
t = NULL;
|
||||
}
|
||||
|
||||
free(t);
|
||||
@ -458,17 +450,127 @@ static int config_parse_service_restart(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int name_load_fragment(Name *n) {
|
||||
#define FOLLOW_MAX 8
|
||||
|
||||
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"
|
||||
static char *build_path(const char *path, const char *filename) {
|
||||
char *e, *r;
|
||||
size_t k;
|
||||
|
||||
assert(path);
|
||||
assert(filename);
|
||||
|
||||
/* This removes the last component of path and appends
|
||||
* filename, unless the latter is absolute anyway or the
|
||||
* former isn't */
|
||||
|
||||
if (filename[0] == '/')
|
||||
return strdup(filename);
|
||||
|
||||
if (!(e = strrchr(path, '/')))
|
||||
return strdup(filename);
|
||||
|
||||
k = strlen(filename);
|
||||
if (!(r = new(char, e-path+1+k+1)))
|
||||
return NULL;
|
||||
|
||||
memcpy(r, path, e-path+1);
|
||||
memcpy(r+(e-path)+1, filename, k+1);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int open_follow(const char **filename, FILE **_f, Set *names) {
|
||||
unsigned c;
|
||||
int fd, r;
|
||||
FILE *f;
|
||||
char *n = NULL;
|
||||
const char *fn;
|
||||
|
||||
assert(filename);
|
||||
assert(*filename);
|
||||
assert(_f);
|
||||
assert(names);
|
||||
|
||||
fn = *filename;
|
||||
|
||||
for (c = 0; c < FOLLOW_MAX; c++) {
|
||||
char *target, *k, *name;
|
||||
|
||||
/* Add the file name we are currently looking at to
|
||||
* the names of this unit */
|
||||
name = file_name_from_path(fn);
|
||||
if (!set_get(names, name)) {
|
||||
|
||||
if (!(name = strdup(name))) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = set_put(names, name)) < 0) {
|
||||
free(name);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
free(name);
|
||||
}
|
||||
|
||||
/* Try to open the file name, but don' if its a symlink */
|
||||
fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
|
||||
if (fd >= 0 || errno != ELOOP)
|
||||
break;
|
||||
|
||||
/* Hmm, so this is a symlink. Let's read the name, and follow it manually */
|
||||
if ((r = readlink_malloc(fn, &target)) < 0)
|
||||
goto finish;
|
||||
|
||||
k = build_path(fn, target);
|
||||
free(target);
|
||||
|
||||
if (!k) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
free(n);
|
||||
fn = n = k;
|
||||
}
|
||||
|
||||
if (c >= FOLLOW_MAX) {
|
||||
r = -ELOOP;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!(f = fdopen(fd, "r"))) {
|
||||
r = -errno;
|
||||
assert(close_nointr(fd) == 0);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
*_f = f;
|
||||
*filename = fn;
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
free(n);
|
||||
return r;
|
||||
}
|
||||
|
||||
int unit_load_fragment(Unit *u) {
|
||||
|
||||
static const char* const section_table[_UNIT_TYPE_MAX] = {
|
||||
[UNIT_SERVICE] = "Service",
|
||||
[UNIT_TIMER] = "Timer",
|
||||
[UNIT_SOCKET] = "Socket",
|
||||
[UNIT_TARGET] = "Target",
|
||||
[UNIT_DEVICE] = "Device",
|
||||
[UNIT_MOUNT] = "Mount",
|
||||
[UNIT_AUTOMOUNT] = "Automount",
|
||||
[UNIT_SNAPSHOT] = "Snapshot"
|
||||
};
|
||||
|
||||
#define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
|
||||
@ -482,73 +584,110 @@ int name_load_fragment(Name *n) {
|
||||
{ "Environment", config_parse_strv, &(context).environment, section }
|
||||
|
||||
const ConfigItem items[] = {
|
||||
{ "Names", config_parse_names, &n->meta.names, "Meta" },
|
||||
{ "Description", config_parse_string, &n->meta.description, "Meta" },
|
||||
{ "Requires", config_parse_deps, UINT_TO_PTR(NAME_REQUIRES), "Meta" },
|
||||
{ "SoftRequires", config_parse_deps, UINT_TO_PTR(NAME_SOFT_REQUIRES), "Meta" },
|
||||
{ "Wants", config_parse_deps, UINT_TO_PTR(NAME_WANTS), "Meta" },
|
||||
{ "Requisite", config_parse_deps, UINT_TO_PTR(NAME_REQUISITE), "Meta" },
|
||||
{ "SoftRequisite", config_parse_deps, UINT_TO_PTR(NAME_SOFT_REQUISITE), "Meta" },
|
||||
{ "Conflicts", config_parse_deps, UINT_TO_PTR(NAME_CONFLICTS), "Meta" },
|
||||
{ "Before", config_parse_deps, UINT_TO_PTR(NAME_BEFORE), "Meta" },
|
||||
{ "After", config_parse_deps, UINT_TO_PTR(NAME_AFTER), "Meta" },
|
||||
{ "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, &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"),
|
||||
{ "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, &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"),
|
||||
{ "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(n->automount.exec_context, "Automount"),
|
||||
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;
|
||||
|
||||
/* Clear the symlink name set first */
|
||||
while ((k = set_steal_first(symlink_names)))
|
||||
free(k);
|
||||
|
||||
/* 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 */
|
||||
if (r == 0)
|
||||
n->meta.id = t;
|
||||
|
||||
return r;
|
||||
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;
|
||||
}
|
||||
|
@ -3,10 +3,10 @@
|
||||
#ifndef fooloadfragmenthfoo
|
||||
#define fooloadfragmenthfoo
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
/* Read service data from .desktop file style configuration fragments */
|
||||
|
||||
int name_load_fragment(Name *n);
|
||||
int unit_load_fragment(Unit *u);
|
||||
|
||||
#endif
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
#include "load-fstab.h"
|
||||
|
||||
int name_load_fstab(Name *n) {
|
||||
assert(n);
|
||||
int unit_load_fstab(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
/* Load dependencies from /etc/fstab */
|
||||
|
||||
|
@ -3,10 +3,10 @@
|
||||
#ifndef fooloadfstabhfoo
|
||||
#define fooloadfstabhfoo
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
/* Read service data from /etc/fstab */
|
||||
|
||||
int name_load_fstab(Name *n);
|
||||
int unit_load_fstab(Unit *u);
|
||||
|
||||
#endif
|
||||
|
8
main.c
8
main.c
@ -10,7 +10,7 @@
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Manager *m = NULL;
|
||||
Name *target = NULL;
|
||||
Unit *target = NULL;
|
||||
Job *job = NULL;
|
||||
int r, retval = 1;
|
||||
|
||||
@ -21,7 +21,7 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = manager_load_name(m, "default.target", &target)) < 0) {
|
||||
if ((r = manager_load_unit(m, "default.target", &target)) < 0) {
|
||||
log_error("Failed to load default target: %s", strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
@ -31,8 +31,8 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("→ By names:\n");
|
||||
manager_dump_names(m, stdout, "\t");
|
||||
printf("→ By units:\n");
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
|
||||
printf("→ By jobs:\n");
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
190
manager.c
190
manager.c
@ -26,7 +26,7 @@ Manager* manager_new(void) {
|
||||
|
||||
m->signal_fd = m->epoll_fd = -1;
|
||||
|
||||
if (!(m->names = hashmap_new(string_hash_func, string_compare_func)))
|
||||
if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
|
||||
goto fail;
|
||||
|
||||
if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
|
||||
@ -63,18 +63,18 @@ fail:
|
||||
}
|
||||
|
||||
void manager_free(Manager *m) {
|
||||
Name *n;
|
||||
Unit *u;
|
||||
Job *j;
|
||||
|
||||
assert(m);
|
||||
|
||||
while ((n = hashmap_first(m->names)))
|
||||
name_free(n);
|
||||
|
||||
while ((j = hashmap_steal_first(m->transaction_jobs)))
|
||||
while ((j = hashmap_first(m->transaction_jobs)))
|
||||
job_free(j);
|
||||
|
||||
hashmap_free(m->names);
|
||||
while ((u = hashmap_first(m->units)))
|
||||
unit_free(u);
|
||||
|
||||
hashmap_free(m->units);
|
||||
hashmap_free(m->jobs);
|
||||
hashmap_free(m->transaction_jobs);
|
||||
hashmap_free(m->watch_pids);
|
||||
@ -99,13 +99,13 @@ static void transaction_delete_job(Manager *m, Job *j) {
|
||||
job_free(j);
|
||||
}
|
||||
|
||||
static void transaction_delete_name(Manager *m, Name *n) {
|
||||
static void transaction_delete_unit(Manager *m, Unit *u) {
|
||||
Job *j;
|
||||
|
||||
/* Deletes all jobs associated with a certain name from the
|
||||
/* Deletes all jobs associated with a certain unit from the
|
||||
* transaction */
|
||||
|
||||
while ((j = hashmap_get(m->transaction_jobs, n)))
|
||||
while ((j = hashmap_get(m->transaction_jobs, u)))
|
||||
transaction_delete_job(m, j);
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsi
|
||||
|
||||
assert(m);
|
||||
|
||||
/* A recursive sweep through the graph that marks all names
|
||||
/* A recursive sweep through the graph that marks all units
|
||||
* that matter to the anchor job, i.e. are directly or
|
||||
* indirectly a dependency of the anchor job via paths that
|
||||
* are fully marked as mattering. */
|
||||
@ -145,7 +145,7 @@ static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsi
|
||||
if (!l->matters)
|
||||
continue;
|
||||
|
||||
/* This name has already been marked */
|
||||
/* This unit has already been marked */
|
||||
if (l->object->generation == generation)
|
||||
continue;
|
||||
|
||||
@ -161,7 +161,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job
|
||||
|
||||
assert(j);
|
||||
assert(other);
|
||||
assert(j->name == other->name);
|
||||
assert(j->unit == other->unit);
|
||||
assert(!j->installed);
|
||||
|
||||
/* Merges 'other' into 'j' and then deletes j. */
|
||||
@ -240,7 +240,7 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) {
|
||||
return -ENOEXEC;
|
||||
|
||||
/* Ok, we can drop one, so let's do so. */
|
||||
log_debug("Try to fix job merging by deleting job %s/%s", name_id(d->name), job_type_to_string(d->type));
|
||||
log_debug("Try to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type));
|
||||
transaction_delete_job(m, d);
|
||||
return 0;
|
||||
}
|
||||
@ -291,8 +291,8 @@ static int transaction_merge_jobs(Manager *m) {
|
||||
assert_se(job_type_merge(&t, k->type) == 0);
|
||||
|
||||
/* If an active job is mergeable, merge it too */
|
||||
if (j->name->meta.job)
|
||||
job_type_merge(&t, j->name->meta.job->type); /* Might fail. Which is OK */
|
||||
if (j->unit->meta.job)
|
||||
job_type_merge(&t, j->unit->meta.job->type); /* Might fail. Which is OK */
|
||||
|
||||
while ((k = j->transaction_next)) {
|
||||
if (j->installed) {
|
||||
@ -309,11 +309,11 @@ static int transaction_merge_jobs(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool name_matters_to_anchor(Name *n, Job *j) {
|
||||
assert(n);
|
||||
static bool unit_matters_to_anchor(Unit *u, Job *j) {
|
||||
assert(u);
|
||||
assert(!j->transaction_prev);
|
||||
|
||||
/* Checks whether at least one of the jobs for this name
|
||||
/* Checks whether at least one of the jobs for this unit
|
||||
* matters to the anchor. */
|
||||
|
||||
LIST_FOREACH(transaction, j, j)
|
||||
@ -325,7 +325,7 @@ static bool name_matters_to_anchor(Name *n, Job *j) {
|
||||
|
||||
static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation) {
|
||||
Iterator i;
|
||||
Name *n;
|
||||
Unit *u;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@ -349,11 +349,11 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned
|
||||
for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) {
|
||||
|
||||
if (!k->installed &&
|
||||
!name_matters_to_anchor(k->name, k)) {
|
||||
!unit_matters_to_anchor(k->unit, k)) {
|
||||
/* Ok, we can drop this one, so let's
|
||||
* do so. */
|
||||
log_debug("Breaking order cycle by deleting job %s/%s", name_id(k->name), job_type_to_string(k->type));
|
||||
transaction_delete_name(m, k->name);
|
||||
log_debug("Breaking order cycle by deleting job %s/%s", unit_id(k->unit), job_type_to_string(k->type));
|
||||
transaction_delete_unit(m, k->unit);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
@ -372,17 +372,17 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned
|
||||
j->generation = generation;
|
||||
|
||||
/* We assume that the the dependencies are bidirectional, and
|
||||
* hence can ignore NAME_AFTER */
|
||||
SET_FOREACH(n, j->name->meta.dependencies[NAME_BEFORE], i) {
|
||||
* hence can ignore UNIT_AFTER */
|
||||
SET_FOREACH(u, j->unit->meta.dependencies[UNIT_BEFORE], i) {
|
||||
Job *o;
|
||||
|
||||
/* Is there a job for this name? */
|
||||
if (!(o = hashmap_get(m->transaction_jobs, n)))
|
||||
/* Is there a job for this unit? */
|
||||
if (!(o = hashmap_get(m->transaction_jobs, u)))
|
||||
|
||||
/* Ok, there is no job for this in the
|
||||
* transaction, but maybe there is already one
|
||||
* running? */
|
||||
if (!(o = n->meta.job))
|
||||
if (!(o = u->meta.job))
|
||||
continue;
|
||||
|
||||
if ((r = transaction_verify_order_one(m, o, j, generation)) < 0)
|
||||
@ -427,7 +427,7 @@ static void transaction_collect_garbage(Manager *m) {
|
||||
if (j->object_list)
|
||||
continue;
|
||||
|
||||
log_debug("Garbage collecting job %s/%s", name_id(j->name), job_type_to_string(j->type));
|
||||
log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type));
|
||||
transaction_delete_job(m, j);
|
||||
again = true;
|
||||
break;
|
||||
@ -451,9 +451,9 @@ static int transaction_is_destructive(Manager *m, JobMode mode) {
|
||||
assert(!j->transaction_prev);
|
||||
assert(!j->transaction_next);
|
||||
|
||||
if (j->name->meta.job &&
|
||||
j->name->meta.job != j &&
|
||||
!job_type_is_superset(j->type, j->name->meta.job->type))
|
||||
if (j->unit->meta.job &&
|
||||
j->unit->meta.job != j &&
|
||||
!job_type_is_superset(j->type, j->unit->meta.job->type))
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
@ -483,12 +483,12 @@ static void transaction_minimize_impact(Manager *m) {
|
||||
/* Would this stop a running service?
|
||||
* Would this change an existing job?
|
||||
* If so, let's drop this entry */
|
||||
if ((j->type != JOB_STOP || NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(j->name))) &&
|
||||
(!j->name->meta.job || job_type_is_conflicting(j->type, j->name->meta.job->state)))
|
||||
if ((j->type != JOB_STOP || UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) &&
|
||||
(!j->unit->meta.job || job_type_is_conflicting(j->type, j->unit->meta.job->state)))
|
||||
continue;
|
||||
|
||||
/* Ok, let's get rid of this */
|
||||
log_debug("Deleting %s/%s to minimize impact", name_id(j->name), job_type_to_string(j->type));
|
||||
log_debug("Deleting %s/%s to minimize impact", unit_id(j->unit), job_type_to_string(j->type));
|
||||
transaction_delete_job(m, j);
|
||||
again = true;
|
||||
break;
|
||||
@ -524,10 +524,10 @@ static int transaction_apply(Manager *m, JobMode mode) {
|
||||
if (j->installed)
|
||||
continue;
|
||||
|
||||
if (j->name->meta.job)
|
||||
job_free(j->name->meta.job);
|
||||
if (j->unit->meta.job)
|
||||
job_free(j->unit->meta.job);
|
||||
|
||||
j->name->meta.job = j;
|
||||
j->unit->meta.job = j;
|
||||
j->installed = true;
|
||||
|
||||
/* We're fully installed. Now let's free data we don't
|
||||
@ -629,21 +629,21 @@ rollback:
|
||||
return r;
|
||||
}
|
||||
|
||||
static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool force, bool *is_new) {
|
||||
static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool force, bool *is_new) {
|
||||
Job *j, *f;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(name);
|
||||
assert(unit);
|
||||
|
||||
/* Looks for an axisting prospective job and returns that. If
|
||||
* it doesn't exist it is created and added to the prospective
|
||||
* jobs list. */
|
||||
|
||||
f = hashmap_get(m->transaction_jobs, name);
|
||||
f = hashmap_get(m->transaction_jobs, unit);
|
||||
|
||||
LIST_FOREACH(transaction, j, f) {
|
||||
assert(j->name == name);
|
||||
assert(j->unit == unit);
|
||||
|
||||
if (j->type == type) {
|
||||
if (is_new)
|
||||
@ -652,9 +652,9 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool f
|
||||
}
|
||||
}
|
||||
|
||||
if (name->meta.job && name->meta.job->type == type)
|
||||
j = name->meta.job;
|
||||
else if (!(j = job_new(m, type, name)))
|
||||
if (unit->meta.job && unit->meta.job->type == type)
|
||||
j = unit->meta.job;
|
||||
else if (!(j = job_new(m, type, unit)))
|
||||
return NULL;
|
||||
|
||||
j->generation = 0;
|
||||
@ -664,7 +664,7 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool f
|
||||
|
||||
LIST_PREPEND(Job, transaction, f, j);
|
||||
|
||||
if ((r = hashmap_replace(m->transaction_jobs, name, f)) < 0) {
|
||||
if ((r = hashmap_replace(m->transaction_jobs, unit, f)) < 0) {
|
||||
job_free(j);
|
||||
return NULL;
|
||||
}
|
||||
@ -682,9 +682,9 @@ void manager_transaction_unlink_job(Manager *m, Job *j) {
|
||||
if (j->transaction_prev)
|
||||
j->transaction_prev->transaction_next = j->transaction_next;
|
||||
else if (j->transaction_next)
|
||||
hashmap_replace(m->transaction_jobs, j->name, j->transaction_next);
|
||||
hashmap_replace(m->transaction_jobs, j->unit, j->transaction_next);
|
||||
else
|
||||
hashmap_remove_value(m->transaction_jobs, j->name, j);
|
||||
hashmap_remove_value(m->transaction_jobs, j->unit, j);
|
||||
|
||||
if (j->transaction_next)
|
||||
j->transaction_next->transaction_prev = j->transaction_prev;
|
||||
@ -701,32 +701,32 @@ void manager_transaction_unlink_job(Manager *m, Job *j) {
|
||||
|
||||
if (other) {
|
||||
log_debug("Deleting job %s/%s as dependency of job %s/%s",
|
||||
name_id(other->name), job_type_to_string(other->type),
|
||||
name_id(j->name), job_type_to_string(j->type));
|
||||
unit_id(other->unit), job_type_to_string(other->type),
|
||||
unit_id(j->unit), job_type_to_string(j->type));
|
||||
transaction_delete_job(m, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int transaction_add_job_and_dependencies(Manager *m, JobType type, Name *name, Job *by, bool matters, bool force, Job **_ret) {
|
||||
static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit *unit, Job *by, bool matters, bool force, Job **_ret) {
|
||||
Job *ret;
|
||||
Iterator i;
|
||||
Name *dep;
|
||||
Unit *dep;
|
||||
int r;
|
||||
bool is_new;
|
||||
|
||||
assert(m);
|
||||
assert(type < _JOB_TYPE_MAX);
|
||||
assert(name);
|
||||
assert(unit);
|
||||
|
||||
if (name->meta.load_state != NAME_LOADED)
|
||||
if (unit->meta.load_state != UNIT_LOADED)
|
||||
return -EINVAL;
|
||||
|
||||
if (!name_job_is_applicable(name, type))
|
||||
if (!unit_job_is_applicable(unit, type))
|
||||
return -EBADR;
|
||||
|
||||
/* First add the job. */
|
||||
if (!(ret = transaction_add_one_job(m, type, name, force, &is_new)))
|
||||
if (!(ret = transaction_add_one_job(m, type, unit, force, &is_new)))
|
||||
return -ENOMEM;
|
||||
|
||||
/* Then, add a link to the job. */
|
||||
@ -736,28 +736,28 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Name *
|
||||
if (is_new) {
|
||||
/* Finally, recursively add in all dependencies. */
|
||||
if (type == JOB_START || type == JOB_RELOAD_OR_START) {
|
||||
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], i)
|
||||
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES], i)
|
||||
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
|
||||
goto fail;
|
||||
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUIRES], i)
|
||||
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUIRES], i)
|
||||
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0 && r != -EBADR)
|
||||
goto fail;
|
||||
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], i)
|
||||
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i)
|
||||
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0 && r != -EBADR)
|
||||
goto fail;
|
||||
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], i)
|
||||
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i)
|
||||
if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
|
||||
goto fail;
|
||||
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], i)
|
||||
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUISITE], i)
|
||||
if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) < 0 && r != -EBADR)
|
||||
goto fail;
|
||||
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], i)
|
||||
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTS], i)
|
||||
if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
|
||||
goto fail;
|
||||
|
||||
} else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
|
||||
|
||||
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], i)
|
||||
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRED_BY], i)
|
||||
if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
|
||||
goto fail;
|
||||
}
|
||||
@ -771,16 +771,16 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, bool force, Job **_ret) {
|
||||
int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret) {
|
||||
int r;
|
||||
Job *ret;
|
||||
|
||||
assert(m);
|
||||
assert(type < _JOB_TYPE_MAX);
|
||||
assert(name);
|
||||
assert(unit);
|
||||
assert(mode < _JOB_MODE_MAX);
|
||||
|
||||
if ((r = transaction_add_job_and_dependencies(m, type, name, NULL, true, force, &ret))) {
|
||||
if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret))) {
|
||||
transaction_abort(m);
|
||||
return r;
|
||||
}
|
||||
@ -800,11 +800,11 @@ Job *manager_get_job(Manager *m, uint32_t id) {
|
||||
return hashmap_get(m->jobs, UINT32_TO_PTR(id));
|
||||
}
|
||||
|
||||
Name *manager_get_name(Manager *m, const char *name) {
|
||||
Unit *manager_get_unit(Manager *m, const char *name) {
|
||||
assert(m);
|
||||
assert(name);
|
||||
|
||||
return hashmap_get(m->names, name);
|
||||
return hashmap_get(m->units, name);
|
||||
}
|
||||
|
||||
static void dispatch_load_queue(Manager *m) {
|
||||
@ -818,20 +818,20 @@ static void dispatch_load_queue(Manager *m) {
|
||||
|
||||
m->dispatching_load_queue = true;
|
||||
|
||||
/* Dispatches the load queue. Takes a name from the queue and
|
||||
/* Dispatches the load queue. Takes a unit from the queue and
|
||||
* tries to load its data until the queue is empty */
|
||||
|
||||
while ((meta = m->load_queue)) {
|
||||
assert(meta->in_load_queue);
|
||||
|
||||
name_load(NAME(meta));
|
||||
unit_load(UNIT(meta));
|
||||
}
|
||||
|
||||
m->dispatching_load_queue = false;
|
||||
}
|
||||
|
||||
int manager_load_name(Manager *m, const char *name, Name **_ret) {
|
||||
Name *ret;
|
||||
int manager_load_unit(Manager *m, const char *name, Unit **_ret) {
|
||||
Unit *ret;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@ -841,20 +841,20 @@ int manager_load_name(Manager *m, const char *name, Name **_ret) {
|
||||
/* This will load the service information files, but not actually
|
||||
* start any services or anything */
|
||||
|
||||
if ((ret = manager_get_name(m, name))) {
|
||||
if ((ret = manager_get_unit(m, name))) {
|
||||
*_ret = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(ret = name_new(m)))
|
||||
if (!(ret = unit_new(m)))
|
||||
return -ENOMEM;
|
||||
|
||||
if ((r = name_add_name(ret, name)) < 0) {
|
||||
name_free(ret);
|
||||
if ((r = unit_add_name(ret, name)) < 0) {
|
||||
unit_free(ret);
|
||||
return r;
|
||||
}
|
||||
|
||||
name_add_to_load_queue(ret);
|
||||
unit_add_to_load_queue(ret);
|
||||
dispatch_load_queue(m);
|
||||
|
||||
*_ret = ret;
|
||||
@ -872,17 +872,17 @@ void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) {
|
||||
job_dump(j, f, prefix);
|
||||
}
|
||||
|
||||
void manager_dump_names(Manager *s, FILE *f, const char *prefix) {
|
||||
void manager_dump_units(Manager *s, FILE *f, const char *prefix) {
|
||||
Iterator i;
|
||||
Name *n;
|
||||
Unit *u;
|
||||
const char *t;
|
||||
|
||||
assert(s);
|
||||
assert(f);
|
||||
|
||||
HASHMAP_FOREACH_KEY(n, t, s->names, i)
|
||||
if (name_id(n) == t)
|
||||
name_dump(n, f, prefix);
|
||||
HASHMAP_FOREACH_KEY(u, t, s->units, i)
|
||||
if (unit_id(u) == t)
|
||||
unit_dump(u, f, prefix);
|
||||
}
|
||||
|
||||
void manager_clear_jobs(Manager *m) {
|
||||
@ -919,7 +919,7 @@ static int manager_dispatch_sigchld(Manager *m) {
|
||||
|
||||
for (;;) {
|
||||
siginfo_t si;
|
||||
Name *n;
|
||||
Unit *u;
|
||||
|
||||
zero(si);
|
||||
if (waitid(P_ALL, 0, &si, WNOHANG) < 0)
|
||||
@ -931,10 +931,10 @@ static int manager_dispatch_sigchld(Manager *m) {
|
||||
if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED)
|
||||
continue;
|
||||
|
||||
if (!(n = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
|
||||
if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
|
||||
continue;
|
||||
|
||||
NAME_VTABLE(n)->sigchld_event(n, si.si_pid, si.si_code, si.si_status);
|
||||
UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -990,21 +990,21 @@ static int process_event(Manager *m, struct epoll_event *ev) {
|
||||
break;
|
||||
|
||||
case MANAGER_FD: {
|
||||
Name *n;
|
||||
Unit *u;
|
||||
|
||||
/* Some fd event, to be dispatched to the names */
|
||||
assert_se(n = ev->data.ptr);
|
||||
NAME_VTABLE(n)->fd_event(n, ev->data.fd, ev->events);
|
||||
/* Some fd event, to be dispatched to the units */
|
||||
assert_se(u = ev->data.ptr);
|
||||
UNIT_VTABLE(u)->fd_event(u, ev->data.fd, ev->events);
|
||||
break;
|
||||
}
|
||||
|
||||
case MANAGER_TIMER: {
|
||||
Name *n;
|
||||
uint64_t u;
|
||||
Unit *u;
|
||||
uint64_t v;
|
||||
ssize_t k;
|
||||
|
||||
/* Some timer event, to be dispatched to the names */
|
||||
if ((k = read(ev->data.fd, &u, sizeof(u))) != sizeof(u)) {
|
||||
/* Some timer event, to be dispatched to the units */
|
||||
if ((k = read(ev->data.fd, &v, sizeof(v))) != sizeof(v)) {
|
||||
|
||||
if (k < 0 && (errno == EINTR || errno == EAGAIN))
|
||||
break;
|
||||
@ -1012,8 +1012,8 @@ static int process_event(Manager *m, struct epoll_event *ev) {
|
||||
return k < 0 ? -errno : -EIO;
|
||||
}
|
||||
|
||||
assert_se(n = ev->data.ptr);
|
||||
NAME_VTABLE(n)->timer_event(n, ev->data.fd, u);
|
||||
assert_se(u = ev->data.ptr);
|
||||
UNIT_VTABLE(u)->timer_event(u, ev->data.fd, v);
|
||||
break;
|
||||
}
|
||||
|
||||
|
22
manager.h
22
manager.h
@ -10,7 +10,7 @@
|
||||
typedef struct Manager Manager;
|
||||
typedef enum ManagerEventType ManagerEventType;
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
#include "job.h"
|
||||
#include "hashmap.h"
|
||||
#include "list.h"
|
||||
@ -25,28 +25,28 @@ enum ManagerEventType {
|
||||
struct Manager {
|
||||
uint32_t current_job_id;
|
||||
|
||||
/* Note that the set of names we know of is allowed to be
|
||||
/* Note that the set of units we know of is allowed to be
|
||||
* incosistent. However the subset of it that is loaded may
|
||||
* not, and the list of jobs may neither. */
|
||||
|
||||
/* Active jobs and names */
|
||||
Hashmap *names; /* name string => Name object n:1 */
|
||||
/* Active jobs and units */
|
||||
Hashmap *units; /* name string => Unit object n:1 */
|
||||
Hashmap *jobs; /* job id => Job object 1:1 */
|
||||
|
||||
/* Names that need to be loaded */
|
||||
/* Units that need to be loaded */
|
||||
LIST_HEAD(Meta, load_queue); /* this is actually more a stack than a queue, but uh. */
|
||||
|
||||
/* Jobs that need to be run */
|
||||
LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */
|
||||
|
||||
/* Jobs to be added */
|
||||
Hashmap *transaction_jobs; /* Name object => Job object list 1:1 */
|
||||
Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */
|
||||
JobDependency *transaction_anchor;
|
||||
|
||||
bool dispatching_load_queue:1;
|
||||
bool dispatching_run_queue:1;
|
||||
|
||||
Hashmap *watch_pids; /* pid => Name object n:1 */
|
||||
Hashmap *watch_pids; /* pid => Unit object n:1 */
|
||||
|
||||
int epoll_fd;
|
||||
int signal_fd;
|
||||
@ -56,12 +56,12 @@ Manager* manager_new(void);
|
||||
void manager_free(Manager *m);
|
||||
|
||||
Job *manager_get_job(Manager *m, uint32_t id);
|
||||
Name *manager_get_name(Manager *m, const char *name);
|
||||
Unit *manager_get_unit(Manager *m, const char *name);
|
||||
|
||||
int manager_load_name(Manager *m, const char *name, Name **_ret);
|
||||
int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, bool force, Job **_ret);
|
||||
int manager_load_unit(Manager *m, const char *name, Unit **_ret);
|
||||
int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret);
|
||||
|
||||
void manager_dump_names(Manager *s, FILE *f, const char *prefix);
|
||||
void manager_dump_units(Manager *s, FILE *f, const char *prefix);
|
||||
void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
|
||||
|
||||
void manager_transaction_unlink_job(Manager *m, Job *j);
|
||||
|
38
mount.c
38
mount.c
@ -2,41 +2,41 @@
|
||||
|
||||
#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",
|
||||
@ -46,7 +46,7 @@ static void mount_dump(Name *n, FILE *f, const char *prefix) {
|
||||
[MOUNT_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
|
||||
Mount *s = MOUNT(n);
|
||||
Mount *s = MOUNT(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -57,20 +57,20 @@ static void mount_dump(Name *n, FILE *f, const char *prefix) {
|
||||
prefix, s->path);
|
||||
}
|
||||
|
||||
static NameActiveState mount_active_state(Name *n) {
|
||||
static UnitActiveState mount_active_state(Unit *u) {
|
||||
|
||||
static const NameActiveState table[_MOUNT_STATE_MAX] = {
|
||||
[MOUNT_DEAD] = NAME_INACTIVE,
|
||||
[MOUNT_MOUNTING] = NAME_ACTIVATING,
|
||||
[MOUNT_MOUNTED] = NAME_ACTIVE,
|
||||
[MOUNT_UNMOUNTING] = NAME_DEACTIVATING,
|
||||
[MOUNT_MAINTAINANCE] = NAME_INACTIVE,
|
||||
static const UnitActiveState table[_MOUNT_STATE_MAX] = {
|
||||
[MOUNT_DEAD] = UNIT_INACTIVE,
|
||||
[MOUNT_MOUNTING] = UNIT_ACTIVATING,
|
||||
[MOUNT_MOUNTED] = UNIT_ACTIVE,
|
||||
[MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
|
||||
[MOUNT_MAINTAINANCE] = UNIT_INACTIVE,
|
||||
};
|
||||
|
||||
return table[MOUNT(n)->state];
|
||||
return table[MOUNT(u)->state];
|
||||
}
|
||||
|
||||
const NameVTable mount_vtable = {
|
||||
const UnitVTable mount_vtable = {
|
||||
.suffix = ".mount",
|
||||
|
||||
.init = mount_init,
|
||||
|
4
mount.h
4
mount.h
@ -5,7 +5,7 @@
|
||||
|
||||
typedef struct Mount Mount;
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum MountState {
|
||||
MOUNT_DEAD,
|
||||
@ -23,6 +23,6 @@ struct Mount {
|
||||
char *path;
|
||||
};
|
||||
|
||||
extern const NameVTable mount_vtable;
|
||||
extern const UnitVTable mount_vtable;
|
||||
|
||||
#endif
|
||||
|
829
name.c
829
name.c
@ -1,829 +0,0 @@
|
||||
/*-*- 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;
|
||||
}
|
236
name.h
236
name.h
@ -1,236 +0,0 @@
|
||||
/*-*- 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
|
164
service.c
164
service.c
@ -3,31 +3,31 @@
|
||||
#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);
|
||||
|
||||
@ -41,16 +41,16 @@ static void service_done(Name *n) {
|
||||
/* This will leak a process, but at least no memory or any of
|
||||
* our resources */
|
||||
if (s->main_pid > 0) {
|
||||
name_unwatch_pid(n, s->main_pid);
|
||||
unit_unwatch_pid(u, s->main_pid);
|
||||
s->main_pid = 0;
|
||||
}
|
||||
|
||||
if (s->control_pid > 0) {
|
||||
name_unwatch_pid(n, s->control_pid);
|
||||
unit_unwatch_pid(u, s->control_pid);
|
||||
s->control_pid = 0;
|
||||
}
|
||||
|
||||
name_unwatch_timer(n, &s->timer_id);
|
||||
unit_unwatch_timer(u, &s->timer_id);
|
||||
}
|
||||
|
||||
static int service_load_sysv(Service *s) {
|
||||
@ -62,9 +62,9 @@ static int service_load_sysv(Service *s) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int service_init(Name *n) {
|
||||
static int service_init(Unit *u) {
|
||||
int r;
|
||||
Service *s = SERVICE(n);
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -84,27 +84,27 @@ static int service_init(Name *n) {
|
||||
s->state = SERVICE_DEAD;
|
||||
|
||||
/* Load a .service file */
|
||||
r = name_load_fragment(n);
|
||||
r = unit_load_fragment(u);
|
||||
|
||||
/* Load a classic init script as a fallback */
|
||||
if (r == -ENOENT)
|
||||
r = service_load_sysv(s);
|
||||
|
||||
if (r < 0) {
|
||||
service_done(n);
|
||||
service_done(u);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Load dropin directory data */
|
||||
if ((r = name_load_dropin(n)) < 0) {
|
||||
service_done(n);
|
||||
if ((r = unit_load_dropin(u)) < 0) {
|
||||
service_done(u);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void service_dump(Name *n, FILE *f, const char *prefix) {
|
||||
static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
static const char* const state_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_DEAD] = "dead",
|
||||
@ -133,7 +133,7 @@ static void service_dump(Name *n, FILE *f, const char *prefix) {
|
||||
};
|
||||
|
||||
ServiceExecCommand c;
|
||||
Service *s = SERVICE(n);
|
||||
Service *s = SERVICE(u);
|
||||
char *prefix2;
|
||||
|
||||
assert(s);
|
||||
@ -216,7 +216,7 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
state != SERVICE_FINAL_SIGTERM &&
|
||||
state != SERVICE_FINAL_SIGKILL &&
|
||||
state != SERVICE_AUTO_RESTART)
|
||||
name_unwatch_timer(NAME(s), &s->timer_id);
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_id);
|
||||
|
||||
if (state != SERVICE_START_POST &&
|
||||
state != SERVICE_RUNNING &&
|
||||
@ -225,7 +225,7 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
state != SERVICE_STOP_SIGTERM &&
|
||||
state != SERVICE_STOP_SIGKILL)
|
||||
if (s->main_pid >= 0) {
|
||||
name_unwatch_pid(NAME(s), s->main_pid);
|
||||
unit_unwatch_pid(UNIT(s), s->main_pid);
|
||||
s->main_pid = 0;
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
state != SERVICE_FINAL_SIGTERM &&
|
||||
state != SERVICE_FINAL_SIGKILL)
|
||||
if (s->control_pid >= 0) {
|
||||
name_unwatch_pid(NAME(s), s->control_pid);
|
||||
unit_unwatch_pid(UNIT(s), s->control_pid);
|
||||
s->control_pid = 0;
|
||||
}
|
||||
|
||||
@ -252,7 +252,7 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
state != SERVICE_STOP_POST)
|
||||
s->control_command = NULL;
|
||||
|
||||
name_notify(NAME(s), state_table[old_state], state_table[s->state]);
|
||||
unit_notify(UNIT(s), state_table[old_state], state_table[s->state]);
|
||||
}
|
||||
|
||||
static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
|
||||
@ -266,21 +266,21 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
|
||||
assert(fds);
|
||||
assert(n_fds);
|
||||
|
||||
SET_FOREACH(t, NAME(s)->meta.names, i) {
|
||||
SET_FOREACH(t, UNIT(s)->meta.names, i) {
|
||||
char *k;
|
||||
Name *p;
|
||||
Unit *p;
|
||||
int *cfds;
|
||||
unsigned cn_fds;
|
||||
|
||||
/* Look for all socket objects that go by any of our
|
||||
* names and collect their fds */
|
||||
* units and collect their fds */
|
||||
|
||||
if (!(k = name_change_suffix(t, ".socket"))) {
|
||||
if (!(k = unit_name_change_suffix(t, ".socket"))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
p = manager_get_name(NAME(s)->meta.manager, k);
|
||||
p = manager_get_unit(UNIT(s)->meta.manager, k);
|
||||
free(k);
|
||||
|
||||
if ((r = socket_collect_fds(SOCKET(p), &cfds, &cn_fds)) < 0)
|
||||
@ -335,15 +335,15 @@ static int service_spawn(Service *s, ExecCommand *c, bool timeout, bool pass_fds
|
||||
goto fail;
|
||||
|
||||
if (timeout) {
|
||||
if ((r = name_watch_timer(NAME(s), s->timeout_usec, &s->timer_id)) < 0)
|
||||
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_id)) < 0)
|
||||
goto fail;
|
||||
} else
|
||||
name_unwatch_timer(NAME(s), &s->timer_id);
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_id);
|
||||
|
||||
if ((r = exec_spawn(c, &s->exec_context, fds, n_fds, &pid)) < 0)
|
||||
goto fail;
|
||||
|
||||
if ((r = name_watch_pid(NAME(s), pid)) < 0)
|
||||
if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
|
||||
/* FIXME: we need to do something here */
|
||||
goto fail;
|
||||
|
||||
@ -356,7 +356,7 @@ fail:
|
||||
free(fds);
|
||||
|
||||
if (timeout)
|
||||
name_unwatch_timer(NAME(s), &s->timer_id);
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_id);
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -372,7 +372,7 @@ static void service_enter_dead(Service *s, bool success, bool allow_restart) {
|
||||
(s->restart == SERVICE_RESTART_ALWAYS ||
|
||||
(s->restart == SERVICE_RESTART_ON_SUCCESS && !s->failure))) {
|
||||
|
||||
if ((r = name_watch_timer(NAME(s), s->restart_usec, &s->timer_id)) < 0)
|
||||
if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_id)) < 0)
|
||||
goto fail;
|
||||
|
||||
service_set_state(s, SERVICE_AUTO_RESTART);
|
||||
@ -382,7 +382,7 @@ static void service_enter_dead(Service *s, bool success, bool allow_restart) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run install restart timer: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run install restart timer: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
service_enter_dead(s, false, false);
|
||||
}
|
||||
|
||||
@ -407,7 +407,7 @@ static void service_enter_stop_post(Service *s, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run stop executable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run stop executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
service_enter_signal(s, SERVICE_FINAL_SIGTERM, false);
|
||||
}
|
||||
|
||||
@ -450,7 +450,7 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to kill processes: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to kill processes: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
|
||||
if (sent) {
|
||||
s->failure = true;
|
||||
@ -480,7 +480,7 @@ static void service_enter_stop(Service *s, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run stop executable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run stop executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
service_enter_signal(s, SERVICE_STOP_SIGTERM, false);
|
||||
}
|
||||
|
||||
@ -500,7 +500,7 @@ static void service_enter_start_post(Service *s) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run start-post executable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run start-post executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
service_enter_stop(s, false);
|
||||
}
|
||||
|
||||
@ -538,7 +538,7 @@ static void service_enter_start(Service *s) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run start exectuable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run start exectuable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
service_enter_stop(s, false);
|
||||
}
|
||||
|
||||
@ -559,7 +559,7 @@ static void service_enter_start_pre(Service *s) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run start-pre executable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run start-pre executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
service_enter_dead(s, false, true);
|
||||
}
|
||||
|
||||
@ -567,16 +567,16 @@ static void service_enter_restart(Service *s) {
|
||||
int r;
|
||||
assert(s);
|
||||
|
||||
if ((r = manager_add_job(NAME(s)->meta.manager, JOB_START, NAME(s), JOB_FAIL, false, NULL)) < 0)
|
||||
if ((r = manager_add_job(UNIT(s)->meta.manager, JOB_START, UNIT(s), JOB_FAIL, false, NULL)) < 0)
|
||||
goto fail;
|
||||
|
||||
log_debug("%s scheduled restart job.", name_id(NAME(s)));
|
||||
log_debug("%s scheduled restart job.", unit_id(UNIT(s)));
|
||||
service_enter_dead(s, true, false);
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
||||
log_warning("%s failed to schedule restart job: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to schedule restart job: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
service_enter_dead(s, false, false);
|
||||
}
|
||||
|
||||
@ -597,7 +597,7 @@ static void service_enter_reload(Service *s) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run reload executable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run reload executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
service_enter_stop(s, false);
|
||||
}
|
||||
|
||||
@ -619,7 +619,7 @@ static void service_run_next(Service *s, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run spawn next executable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run spawn next executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
|
||||
if (s->state == SERVICE_STOP)
|
||||
service_enter_stop_post(s, false);
|
||||
@ -629,8 +629,8 @@ fail:
|
||||
service_enter_stop(s, false);
|
||||
}
|
||||
|
||||
static int service_start(Name *n) {
|
||||
Service *s = SERVICE(n);
|
||||
static int service_start(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -659,8 +659,8 @@ static int service_start(Name *n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_stop(Name *n) {
|
||||
Service *s = SERVICE(n);
|
||||
static int service_stop(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -681,8 +681,8 @@ static int service_stop(Name *n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_reload(Name *n) {
|
||||
Service *s = SERVICE(n);
|
||||
static int service_reload(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -692,18 +692,18 @@ static int service_reload(Name *n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool service_can_reload(Name *n) {
|
||||
Service *s = SERVICE(n);
|
||||
static bool service_can_reload(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
return !!s->exec_command[SERVICE_EXEC_RELOAD];
|
||||
}
|
||||
|
||||
static NameActiveState service_active_state(Name *n) {
|
||||
assert(n);
|
||||
static UnitActiveState service_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_table[SERVICE(n)->state];
|
||||
return state_table[SERVICE(u)->state];
|
||||
}
|
||||
|
||||
static int main_pid_good(Service *s) {
|
||||
@ -727,8 +727,8 @@ static bool control_pid_good(Service *s) {
|
||||
return s->control_pid > 0;
|
||||
}
|
||||
|
||||
static void service_sigchld_event(Name *n, pid_t pid, int code, int status) {
|
||||
Service *s = SERVICE(n);
|
||||
static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
Service *s = SERVICE(u);
|
||||
bool success;
|
||||
|
||||
assert(s);
|
||||
@ -747,7 +747,7 @@ static void service_sigchld_event(Name *n, pid_t pid, int code, int status) {
|
||||
s->exec_command[SERVICE_EXEC_START]->exec_status = s->main_exec_status;
|
||||
}
|
||||
|
||||
log_debug("%s: main process exited, code=%s status=%i", name_id(n), sigchld_code(code), status);
|
||||
log_debug("%s: main process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status);
|
||||
|
||||
/* The service exited, so the service is officially
|
||||
* gone. */
|
||||
@ -784,7 +784,7 @@ static void service_sigchld_event(Name *n, pid_t pid, int code, int status) {
|
||||
exec_status_fill(&s->control_command->exec_status, pid, code, status);
|
||||
s->control_pid = 0;
|
||||
|
||||
log_debug("%s: control process exited, code=%s status=%i", name_id(n), sigchld_code(code), status);
|
||||
log_debug("%s: control process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status);
|
||||
|
||||
/* If we are shutting things down anyway we
|
||||
* don't care about failing commands. */
|
||||
@ -840,7 +840,7 @@ static void service_sigchld_event(Name *n, pid_t pid, int code, int status) {
|
||||
* executed. */
|
||||
|
||||
if ((r = service_load_pid_file(s)) < 0)
|
||||
log_warning("%s: failed to load PID file %s: %s", name_id(NAME(s)), s->pid_file, strerror(-r));
|
||||
log_warning("%s: failed to load PID file %s: %s", unit_id(UNIT(s)), s->pid_file, strerror(-r));
|
||||
}
|
||||
|
||||
/* Fall through */
|
||||
@ -888,8 +888,8 @@ static void service_sigchld_event(Name *n, pid_t pid, int code, int status) {
|
||||
assert_not_reached("Got SIGCHLD for unkown PID");
|
||||
}
|
||||
|
||||
static void service_timer_event(Name *n, int id, uint64_t elapsed) {
|
||||
Service *s = SERVICE(n);
|
||||
static void service_timer_event(Unit *u, int id, uint64_t elapsed) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
assert(elapsed == 1);
|
||||
@ -902,17 +902,17 @@ static void service_timer_event(Name *n, int id, uint64_t elapsed) {
|
||||
case SERVICE_START:
|
||||
case SERVICE_START_POST:
|
||||
case SERVICE_RELOAD:
|
||||
log_warning("%s operation timed out. Stopping.", name_id(n));
|
||||
log_warning("%s operation timed out. Stopping.", unit_id(u));
|
||||
service_enter_stop(s, false);
|
||||
break;
|
||||
|
||||
case SERVICE_STOP:
|
||||
log_warning("%s stopping timed out. Terminating.", name_id(n));
|
||||
log_warning("%s stopping timed out. Terminating.", unit_id(u));
|
||||
service_enter_signal(s, SERVICE_STOP_SIGTERM, false);
|
||||
break;
|
||||
|
||||
case SERVICE_STOP_SIGTERM:
|
||||
log_warning("%s stopping timed out. Killing.", name_id(n));
|
||||
log_warning("%s stopping timed out. Killing.", unit_id(u));
|
||||
service_enter_signal(s, SERVICE_STOP_SIGKILL, false);
|
||||
break;
|
||||
|
||||
@ -921,27 +921,27 @@ static void service_timer_event(Name *n, int id, uint64_t elapsed) {
|
||||
* Must be something we cannot kill, so let's just be
|
||||
* weirded out and continue */
|
||||
|
||||
log_warning("%s still around after SIGKILL. Ignoring.", name_id(n));
|
||||
log_warning("%s still around after SIGKILL. Ignoring.", unit_id(u));
|
||||
service_enter_stop_post(s, false);
|
||||
break;
|
||||
|
||||
case SERVICE_STOP_POST:
|
||||
log_warning("%s stopping timed out (2). Terminating.", name_id(n));
|
||||
log_warning("%s stopping timed out (2). Terminating.", unit_id(u));
|
||||
service_enter_signal(s, SERVICE_FINAL_SIGTERM, false);
|
||||
break;
|
||||
|
||||
case SERVICE_FINAL_SIGTERM:
|
||||
log_warning("%s stopping timed out (2). Killing.", name_id(n));
|
||||
log_warning("%s stopping timed out (2). Killing.", unit_id(u));
|
||||
service_enter_signal(s, SERVICE_FINAL_SIGKILL, false);
|
||||
break;
|
||||
|
||||
case SERVICE_FINAL_SIGKILL:
|
||||
log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", name_id(n));
|
||||
log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", unit_id(u));
|
||||
service_enter_dead(s, false, true);
|
||||
break;
|
||||
|
||||
case SERVICE_AUTO_RESTART:
|
||||
log_debug("%s holdoff time over, scheduling restart.", name_id(n));
|
||||
log_debug("%s holdoff time over, scheduling restart.", unit_id(u));
|
||||
service_enter_restart(s);
|
||||
break;
|
||||
|
||||
@ -950,7 +950,7 @@ static void service_timer_event(Name *n, int id, uint64_t elapsed) {
|
||||
}
|
||||
}
|
||||
|
||||
const NameVTable service_vtable = {
|
||||
const UnitVTable service_vtable = {
|
||||
.suffix = ".service",
|
||||
|
||||
.init = service_init,
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
typedef struct Service Service;
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum ServiceState {
|
||||
SERVICE_DEAD,
|
||||
@ -73,6 +73,6 @@ struct Service {
|
||||
int timer_id;
|
||||
};
|
||||
|
||||
const NameVTable service_vtable;
|
||||
const UnitVTable service_vtable;
|
||||
|
||||
#endif
|
||||
|
12
snapshot.c
12
snapshot.c
@ -1,21 +1,21 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
#include "snapshot.h"
|
||||
|
||||
static void snapshot_done(Name *n) {
|
||||
Snapshot *s = SNAPSHOT(n);
|
||||
static void snapshot_done(Unit *u) {
|
||||
Snapshot *s = SNAPSHOT(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
/* Nothing here for now */
|
||||
}
|
||||
|
||||
static NameActiveState snapshot_active_state(Name *n) {
|
||||
return SNAPSHOT(n)->state == SNAPSHOT_DEAD ? NAME_INACTIVE : NAME_ACTIVE;
|
||||
static UnitActiveState snapshot_active_state(Unit *u) {
|
||||
return SNAPSHOT(u)->state == SNAPSHOT_DEAD ? UNIT_INACTIVE : UNIT_ACTIVE;
|
||||
}
|
||||
|
||||
const NameVTable snapshot_vtable = {
|
||||
const UnitVTable snapshot_vtable = {
|
||||
.suffix = ".snapshot",
|
||||
|
||||
.done = snapshot_done,
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
typedef struct Snapshot Snapshot;
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum SnapshotState {
|
||||
SNAPSHOT_DEAD,
|
||||
@ -19,6 +19,6 @@ struct Snapshot {
|
||||
bool cleanup:1;
|
||||
};
|
||||
|
||||
extern const NameVTable snapshot_vtable;
|
||||
extern const UnitVTable snapshot_vtable;
|
||||
|
||||
#endif
|
||||
|
138
socket.c
138
socket.c
@ -8,27 +8,27 @@
|
||||
#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);
|
||||
@ -47,17 +47,17 @@ static void socket_done(Name *n) {
|
||||
s->control_command = NULL;
|
||||
|
||||
if (s->control_pid > 0) {
|
||||
name_unwatch_pid(n, s->control_pid);
|
||||
unit_unwatch_pid(u, s->control_pid);
|
||||
s->control_pid = 0;
|
||||
}
|
||||
|
||||
s->service = NULL;
|
||||
|
||||
name_unwatch_timer(n, &s->timer_id);
|
||||
unit_unwatch_timer(u, &s->timer_id);
|
||||
}
|
||||
|
||||
static int socket_init(Name *n) {
|
||||
Socket *s = SOCKET(n);
|
||||
static int socket_init(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
char *t;
|
||||
int r;
|
||||
|
||||
@ -71,27 +71,27 @@ static int socket_init(Name *n) {
|
||||
s->timeout_usec = DEFAULT_TIMEOUT_USEC;
|
||||
exec_context_init(&s->exec_context);
|
||||
|
||||
if ((r = name_load_fragment_and_dropin(n)) < 0)
|
||||
if ((r = unit_load_fragment_and_dropin(u)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (!(t = name_change_suffix(name_id(n), ".service"))) {
|
||||
if (!(t = unit_name_change_suffix(unit_id(u), ".service"))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = manager_load_name(n->meta.manager, t, (Name**) &s->service);
|
||||
r = manager_load_unit(u->meta.manager, t, (Unit**) &s->service);
|
||||
free(t);
|
||||
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if ((r = name_add_dependency(n, NAME_BEFORE, NAME(s->service))) < 0)
|
||||
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
socket_done(n);
|
||||
socket_done(u);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ static const char* listen_lookup(int type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void socket_dump(Name *n, FILE *f, const char *prefix) {
|
||||
static void socket_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
static const char* const state_table[_SOCKET_STATE_MAX] = {
|
||||
[SOCKET_DEAD] = "dead",
|
||||
@ -133,7 +133,7 @@ static void socket_dump(Name *n, FILE *f, const char *prefix) {
|
||||
};
|
||||
|
||||
SocketExecCommand c;
|
||||
Socket *s = SOCKET(n);
|
||||
Socket *s = SOCKET(u);
|
||||
SocketPort *p;
|
||||
|
||||
assert(s);
|
||||
@ -183,7 +183,7 @@ static void socket_close_fds(Socket *s) {
|
||||
if (p->fd < 0)
|
||||
continue;
|
||||
|
||||
name_unwatch_fd(NAME(s), p->fd);
|
||||
unit_unwatch_fd(UNIT(s), p->fd);
|
||||
assert_se(close_nointr(p->fd) >= 0);
|
||||
|
||||
p->fd = -1;
|
||||
@ -250,7 +250,7 @@ static void socket_unwatch_fds(Socket *s) {
|
||||
if (p->fd < 0)
|
||||
continue;
|
||||
|
||||
name_unwatch_fd(NAME(s), p->fd);
|
||||
unit_unwatch_fd(UNIT(s), p->fd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,7 +264,7 @@ static int socket_watch_fds(Socket *s) {
|
||||
if (p->fd < 0)
|
||||
continue;
|
||||
|
||||
if ((r = name_watch_fd(NAME(s), p->fd, POLLIN)) < 0)
|
||||
if ((r = unit_watch_fd(UNIT(s), p->fd, POLLIN)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -290,7 +290,7 @@ static void socket_set_state(Socket *s, SocketState state) {
|
||||
state != SOCKET_STOP_POST &&
|
||||
state != SOCKET_STOP_POST_SIGTERM &&
|
||||
state != SOCKET_STOP_POST_SIGKILL)
|
||||
name_unwatch_timer(NAME(s), &s->timer_id);
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_id);
|
||||
|
||||
if (state != SOCKET_START_PRE &&
|
||||
state != SOCKET_START_POST &&
|
||||
@ -301,7 +301,7 @@ static void socket_set_state(Socket *s, SocketState state) {
|
||||
state != SOCKET_STOP_POST_SIGTERM &&
|
||||
state != SOCKET_STOP_POST_SIGKILL)
|
||||
if (s->control_pid >= 0) {
|
||||
name_unwatch_pid(NAME(s), s->control_pid);
|
||||
unit_unwatch_pid(UNIT(s), s->control_pid);
|
||||
s->control_pid = 0;
|
||||
}
|
||||
|
||||
@ -322,7 +322,7 @@ static void socket_set_state(Socket *s, SocketState state) {
|
||||
if (state != SOCKET_LISTENING)
|
||||
socket_unwatch_fds(s);
|
||||
|
||||
name_notify(NAME(s), state_table[old_state], state_table[s->state]);
|
||||
unit_notify(UNIT(s), state_table[old_state], state_table[s->state]);
|
||||
}
|
||||
|
||||
static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
|
||||
@ -334,15 +334,15 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
|
||||
assert(_pid);
|
||||
|
||||
if (timeout) {
|
||||
if ((r = name_watch_timer(NAME(s), s->timeout_usec, &s->timer_id)) < 0)
|
||||
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_id)) < 0)
|
||||
goto fail;
|
||||
} else
|
||||
name_unwatch_timer(NAME(s), &s->timer_id);
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_id);
|
||||
|
||||
if ((r = exec_spawn(c, &s->exec_context, NULL, 0, &pid)) < 0)
|
||||
goto fail;
|
||||
|
||||
if ((r = name_watch_pid(NAME(s), pid)) < 0)
|
||||
if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
|
||||
/* FIXME: we need to do something here */
|
||||
goto fail;
|
||||
|
||||
@ -352,7 +352,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
|
||||
|
||||
fail:
|
||||
if (timeout)
|
||||
name_unwatch_timer(NAME(s), &s->timer_id);
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_id);
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -385,7 +385,7 @@ static void socket_enter_stop_post(Socket *s, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run stop-post executable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run stop-post executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
socket_enter_dead(s, false);
|
||||
}
|
||||
|
||||
@ -414,7 +414,7 @@ static void socket_enter_signal(Socket *s, SocketState state, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to kill processes: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to kill processes: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
|
||||
if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL)
|
||||
socket_enter_stop_post(s, false);
|
||||
@ -441,7 +441,7 @@ static void socket_enter_stop_pre(Socket *s, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run stop-pre executable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run stop-pre executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
socket_enter_stop_post(s, false);
|
||||
}
|
||||
|
||||
@ -451,14 +451,14 @@ static void socket_enter_start_post(Socket *s) {
|
||||
|
||||
if ((r = socket_open_fds(s)) < 0 ||
|
||||
(r = socket_watch_fds(s)) < 0) {
|
||||
log_warning("%s failed to listen on sockets: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to listen on sockets: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) {
|
||||
|
||||
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0) {
|
||||
log_warning("%s failed to run start-post executable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run start-post executable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -488,7 +488,7 @@ static void socket_enter_start_pre(Socket *s) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run start-pre exectuable: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to run start-pre exectuable: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
socket_enter_dead(s, false);
|
||||
}
|
||||
|
||||
@ -497,14 +497,14 @@ static void socket_enter_running(Socket *s) {
|
||||
|
||||
assert(s);
|
||||
|
||||
if ((r = manager_add_job(NAME(s)->meta.manager, JOB_START, NAME(s->service), JOB_REPLACE, true, NULL)) < 0)
|
||||
if ((r = manager_add_job(UNIT(s)->meta.manager, JOB_START, UNIT(s->service), JOB_REPLACE, true, NULL)) < 0)
|
||||
goto fail;
|
||||
|
||||
socket_set_state(s, SOCKET_RUNNING);
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to queue socket startup job: %s", name_id(NAME(s)), strerror(-r));
|
||||
log_warning("%s failed to queue socket startup job: %s", unit_id(UNIT(s)), strerror(-r));
|
||||
socket_enter_dead(s, false);
|
||||
}
|
||||
|
||||
@ -534,8 +534,8 @@ fail:
|
||||
socket_enter_stop_pre(s, false);
|
||||
}
|
||||
|
||||
static int socket_start(Name *n) {
|
||||
Socket *s = SOCKET(n);
|
||||
static int socket_start(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -554,7 +554,7 @@ static int socket_start(Name *n) {
|
||||
return 0;
|
||||
|
||||
/* Cannot run this without the service being around */
|
||||
if (s->service->meta.load_state != NAME_LOADED)
|
||||
if (s->service->meta.load_state != UNIT_LOADED)
|
||||
return -ENOENT;
|
||||
|
||||
assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE);
|
||||
@ -564,8 +564,8 @@ static int socket_start(Name *n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socket_stop(Name *n) {
|
||||
Socket *s = SOCKET(n);
|
||||
static int socket_stop(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -581,18 +581,18 @@ static int socket_stop(Name *n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NameActiveState socket_active_state(Name *n) {
|
||||
assert(n);
|
||||
static UnitActiveState socket_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_table[SOCKET(n)->state];
|
||||
return state_table[SOCKET(u)->state];
|
||||
}
|
||||
|
||||
static void socket_fd_event(Name *n, int fd, uint32_t events) {
|
||||
Socket *s = SOCKET(n);
|
||||
static void socket_fd_event(Unit *u, int fd, uint32_t events) {
|
||||
Socket *s = SOCKET(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
log_info("Incoming traffic on %s", name_id(n));
|
||||
log_info("Incoming traffic on %s", unit_id(u));
|
||||
|
||||
if (events != POLLIN)
|
||||
socket_enter_stop_pre(s, false);
|
||||
@ -600,8 +600,8 @@ static void socket_fd_event(Name *n, int fd, uint32_t events) {
|
||||
socket_enter_running(s);
|
||||
}
|
||||
|
||||
static void socket_sigchld_event(Name *n, pid_t pid, int code, int status) {
|
||||
Socket *s = SOCKET(n);
|
||||
static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
Socket *s = SOCKET(u);
|
||||
bool success;
|
||||
|
||||
assert(s);
|
||||
@ -616,7 +616,7 @@ static void socket_sigchld_event(Name *n, pid_t pid, int code, int status) {
|
||||
exec_status_fill(&s->control_command->exec_status, pid, code, status);
|
||||
s->control_pid = 0;
|
||||
|
||||
log_debug("%s: control process exited, code=%s status=%i", name_id(n), sigchld_code(code), status);
|
||||
log_debug("%s: control process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status);
|
||||
|
||||
if (s->control_command->command_next &&
|
||||
(success || (s->state == SOCKET_EXEC_STOP_PRE || s->state == SOCKET_EXEC_STOP_POST)))
|
||||
@ -659,8 +659,8 @@ static void socket_sigchld_event(Name *n, pid_t pid, int code, int status) {
|
||||
}
|
||||
}
|
||||
|
||||
static void socket_timer_event(Name *n, int id, uint64_t elapsed) {
|
||||
Socket *s = SOCKET(n);
|
||||
static void socket_timer_event(Unit *u, int id, uint64_t elapsed) {
|
||||
Socket *s = SOCKET(u);
|
||||
|
||||
assert(s);
|
||||
assert(elapsed == 1);
|
||||
@ -671,37 +671,37 @@ static void socket_timer_event(Name *n, int id, uint64_t elapsed) {
|
||||
|
||||
case SOCKET_START_PRE:
|
||||
case SOCKET_START_POST:
|
||||
log_warning("%s operation timed out. Stopping.", name_id(n));
|
||||
log_warning("%s operation timed out. Stopping.", unit_id(u));
|
||||
socket_enter_stop_pre(s, false);
|
||||
break;
|
||||
|
||||
case SOCKET_STOP_PRE:
|
||||
log_warning("%s stopping timed out. Terminating.", name_id(n));
|
||||
log_warning("%s stopping timed out. Terminating.", unit_id(u));
|
||||
socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, false);
|
||||
break;
|
||||
|
||||
case SOCKET_STOP_PRE_SIGTERM:
|
||||
log_warning("%s stopping timed out. Killing.", name_id(n));
|
||||
log_warning("%s stopping timed out. Killing.", unit_id(u));
|
||||
socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, false);
|
||||
break;
|
||||
|
||||
case SOCKET_STOP_PRE_SIGKILL:
|
||||
log_warning("%s still around after SIGKILL. Ignoring.", name_id(n));
|
||||
log_warning("%s still around after SIGKILL. Ignoring.", unit_id(u));
|
||||
socket_enter_stop_post(s, false);
|
||||
break;
|
||||
|
||||
case SOCKET_STOP_POST:
|
||||
log_warning("%s stopping timed out (2). Terminating.", name_id(n));
|
||||
log_warning("%s stopping timed out (2). Terminating.", unit_id(u));
|
||||
socket_enter_signal(s, SOCKET_STOP_POST_SIGTERM, false);
|
||||
break;
|
||||
|
||||
case SOCKET_STOP_POST_SIGTERM:
|
||||
log_warning("%s stopping timed out (2). Killing.", name_id(n));
|
||||
log_warning("%s stopping timed out (2). Killing.", unit_id(u));
|
||||
socket_enter_signal(s, SOCKET_STOP_POST_SIGKILL, false);
|
||||
break;
|
||||
|
||||
case SOCKET_STOP_POST_SIGKILL:
|
||||
log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", name_id(n));
|
||||
log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", unit_id(u));
|
||||
socket_enter_dead(s, false);
|
||||
break;
|
||||
|
||||
@ -742,7 +742,7 @@ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const NameVTable socket_vtable = {
|
||||
const UnitVTable socket_vtable = {
|
||||
.suffix = ".socket",
|
||||
|
||||
.init = socket_init,
|
||||
|
4
socket.h
4
socket.h
@ -5,7 +5,7 @@
|
||||
|
||||
typedef struct Socket Socket;
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
typedef enum SocketState {
|
||||
@ -78,6 +78,6 @@ struct Socket {
|
||||
/* Called from the service code when collecting fds */
|
||||
int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds);
|
||||
|
||||
extern const NameVTable socket_vtable;
|
||||
extern const UnitVTable socket_vtable;
|
||||
|
||||
#endif
|
||||
|
14
target.c
14
target.c
@ -1,25 +1,25 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
#include "target.h"
|
||||
#include "load-fragment.h"
|
||||
|
||||
static void target_done(Name *n) {
|
||||
Target *m = TARGET(n);
|
||||
static void target_done(Unit *u) {
|
||||
Target *m = TARGET(u);
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Nothing here for now */
|
||||
}
|
||||
|
||||
static NameActiveState target_active_state(Name *n) {
|
||||
return TARGET(n)->state == TARGET_DEAD ? NAME_INACTIVE : NAME_ACTIVE;
|
||||
static UnitActiveState target_active_state(Unit *u) {
|
||||
return TARGET(u)->state == TARGET_DEAD ? UNIT_INACTIVE : UNIT_ACTIVE;
|
||||
}
|
||||
|
||||
const NameVTable target_vtable = {
|
||||
const UnitVTable target_vtable = {
|
||||
.suffix = ".target",
|
||||
|
||||
.init = name_load_fragment,
|
||||
.init = unit_load_fragment,
|
||||
.done = target_done,
|
||||
|
||||
.active_state = target_active_state
|
||||
|
4
target.h
4
target.h
@ -5,7 +5,7 @@
|
||||
|
||||
typedef struct Target Target;
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum TargetState {
|
||||
TARGET_DEAD,
|
||||
@ -18,6 +18,6 @@ struct Target {
|
||||
TargetState state;
|
||||
};
|
||||
|
||||
extern const NameVTable target_vtable;
|
||||
extern const UnitVTable target_vtable;
|
||||
|
||||
#endif
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Manager *m = NULL;
|
||||
Name *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL;
|
||||
Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL;
|
||||
Job *j;
|
||||
|
||||
assert_se(chdir("test2") == 0);
|
||||
@ -17,10 +17,10 @@ int main(int argc, char *argv[]) {
|
||||
assert_se(m = manager_new());
|
||||
|
||||
printf("Load1:\n");
|
||||
assert_se(manager_load_name(m, "a.service", &a) == 0);
|
||||
assert_se(manager_load_name(m, "b.service", &b) == 0);
|
||||
assert_se(manager_load_name(m, "c.service", &c) == 0);
|
||||
manager_dump_names(m, stdout, "\t");
|
||||
assert_se(manager_load_unit(m, "a.service", &a) == 0);
|
||||
assert_se(manager_load_unit(m, "b.service", &b) == 0);
|
||||
assert_se(manager_load_unit(m, "c.service", &c) == 0);
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
|
||||
printf("Test1: (Trivial)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, c, JOB_REPLACE, false, &j) == 0);
|
||||
@ -28,9 +28,9 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
printf("Load2:\n");
|
||||
manager_clear_jobs(m);
|
||||
assert_se(manager_load_name(m, "d.service", &d) == 0);
|
||||
assert_se(manager_load_name(m, "e.service", &e) == 0);
|
||||
manager_dump_names(m, stdout, "\t");
|
||||
assert_se(manager_load_unit(m, "d.service", &d) == 0);
|
||||
assert_se(manager_load_unit(m, "e.service", &e) == 0);
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
|
||||
printf("Test2: (Cyclic Order, Unfixable)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, &j) == -ENOEXEC);
|
||||
@ -45,8 +45,8 @@ int main(int argc, char *argv[]) {
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
printf("Load3:\n");
|
||||
assert_se(manager_load_name(m, "g.service", &g) == 0);
|
||||
manager_dump_names(m, stdout, "\t");
|
||||
assert_se(manager_load_unit(m, "g.service", &g) == 0);
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
|
||||
printf("Test5: (Colliding transaction, fail)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, false, &j) == -EEXIST);
|
||||
@ -67,8 +67,8 @@ int main(int argc, char *argv[]) {
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
printf("Load4:\n");
|
||||
assert_se(manager_load_name(m, "h.service", &h) == 0);
|
||||
manager_dump_names(m, stdout, "\t");
|
||||
assert_se(manager_load_unit(m, "h.service", &h) == 0);
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
|
||||
printf("Test10: (Unmeargable job type of auxiliary job, fail)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, false, &j) == 0);
|
||||
|
22
timer.c
22
timer.c
@ -1,29 +1,29 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
#include "timer.h"
|
||||
|
||||
static void timer_done(Name *n) {
|
||||
Timer *t = TIMER(n);
|
||||
static void timer_done(Unit *u) {
|
||||
Timer *t = TIMER(u);
|
||||
|
||||
assert(t);
|
||||
}
|
||||
|
||||
static NameActiveState timer_active_state(Name *n) {
|
||||
static UnitActiveState timer_active_state(Unit *u) {
|
||||
|
||||
static const NameActiveState table[_TIMER_STATE_MAX] = {
|
||||
[TIMER_DEAD] = NAME_INACTIVE,
|
||||
[TIMER_WAITING] = NAME_ACTIVE,
|
||||
[TIMER_RUNNING] = NAME_ACTIVE
|
||||
static const UnitActiveState table[_TIMER_STATE_MAX] = {
|
||||
[TIMER_DEAD] = UNIT_INACTIVE,
|
||||
[TIMER_WAITING] = UNIT_ACTIVE,
|
||||
[TIMER_RUNNING] = UNIT_ACTIVE
|
||||
};
|
||||
|
||||
return table[TIMER(n)->state];
|
||||
return table[TIMER(u)->state];
|
||||
}
|
||||
|
||||
const NameVTable timer_vtable = {
|
||||
const UnitVTable timer_vtable = {
|
||||
.suffix = ".timer",
|
||||
|
||||
.init = name_load_fragment_and_dropin,
|
||||
.init = unit_load_fragment_and_dropin,
|
||||
.done = timer_done,
|
||||
|
||||
.active_state = timer_active_state
|
||||
|
4
timer.h
4
timer.h
@ -5,7 +5,7 @@
|
||||
|
||||
typedef struct Timer Timer;
|
||||
|
||||
#include "name.h"
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum TimerState {
|
||||
TIMER_DEAD,
|
||||
@ -25,6 +25,6 @@ struct Timer {
|
||||
Service *service;
|
||||
};
|
||||
|
||||
const NameVTable timer_vtable;
|
||||
const UnitVTable timer_vtable;
|
||||
|
||||
#endif
|
||||
|
807
unit.c
Normal file
807
unit.c
Normal file
@ -0,0 +1,807 @@
|
||||
/*-*- 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;
|
||||
}
|
235
unit.h
Normal file
235
unit.h
Normal file
@ -0,0 +1,235 @@
|
||||
/*-*- 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
|
41
util.c
41
util.c
@ -402,3 +402,44 @@ char *strappend(const char *s, const char *suffix) {
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int readlink_malloc(const char *p, char **r) {
|
||||
size_t l = 100;
|
||||
|
||||
assert(p);
|
||||
assert(r);
|
||||
|
||||
for (;;) {
|
||||
char *c;
|
||||
ssize_t n;
|
||||
|
||||
if (!(c = new(char, l)))
|
||||
return -ENOMEM;
|
||||
|
||||
if ((n = readlink(p, c, l-1)) < 0) {
|
||||
int ret = -errno;
|
||||
free(c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((size_t) n < l-1) {
|
||||
c[n] = 0;
|
||||
*r = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(c);
|
||||
l *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
char *file_name_from_path(const char *p) {
|
||||
char *r;
|
||||
|
||||
assert(p);
|
||||
|
||||
if ((r = strrchr(p, '/')))
|
||||
return r + 1;
|
||||
|
||||
return (char*) p;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user