first attempt in implementinging execution logic

This commit is contained in:
Lennart Poettering 2010-01-23 01:52:57 +01:00
parent cd2dbd7df9
commit 5cb5a6ffc3
33 changed files with 1788 additions and 555 deletions

View file

@ -1,7 +1,29 @@
CFLAGS=-Wall -Wextra -O0 -g -pipe -D_GNU_SOURCE -fdiagnostics-show-option -Wno-unused-parameter
LIBS=-lrt
LIBS=-lrt -lcap
COMMON=name.o util.o set.o hashmap.o strv.o job.o manager.o conf-parser.o load-fragment.o socket-util.o log.o
COMMON= \
name.o \
util.o \
set.o \
hashmap.o \
strv.o \
job.o \
manager.o \
conf-parser.o \
load-fragment.o \
socket-util.o \
log.o \
service.o \
automount.o \
mount.o \
device.o \
milestone.o \
snapshot.o \
socket.o \
timer.o \
load-fstab.o \
load-dropin.o \
execute.o
all: systemd test-engine test-job-type

111
automount.c Normal file
View file

@ -0,0 +1,111 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include <errno.h>
#include "name.h"
#include "automount.h"
#include "load-fragment.h"
#include "load-fstab.h"
#include "load-dropin.h"
static int automount_load(Name *n) {
int r;
Automount *a = AUTOMOUNT(n);
assert(a);
exec_context_defaults(&a->exec_context);
/* Load a .automount file */
if ((r = name_load_fragment(n)) < 0 && errno != -ENOENT)
return r;
/* Load entry from /etc/fstab */
if ((r = name_load_fstab(n)) < 0)
return r;
/* Load drop-in directory data */
if ((r = name_load_dropin(n)) < 0)
return r;
return 0;
}
static void automount_dump(Name *n, FILE *f, const char *prefix) {
static const char* const state_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_START_PRE] = "start-pre",
[AUTOMOUNT_START_POST] = "start-post",
[AUTOMOUNT_WAITING] = "waiting",
[AUTOMOUNT_RUNNING] = "running",
[AUTOMOUNT_STOP_PRE] = "stop-pre",
[AUTOMOUNT_STOP_POST] = "stop-post",
[AUTOMOUNT_MAINTAINANCE] = "maintainance"
};
static const char* const command_table[_AUTOMOUNT_EXEC_MAX] = {
[AUTOMOUNT_EXEC_START_PRE] = "StartPre",
[AUTOMOUNT_EXEC_START_POST] = "StartPost",
[AUTOMOUNT_EXEC_STOP_PRE] = "StopPre",
[AUTOMOUNT_EXEC_STOP_POST] = "StopPost"
};
AutomountExecCommand c;
Automount *s = AUTOMOUNT(n);
assert(s);
fprintf(f,
"%sAutomount State: %s\n"
"%sPath: %s\n",
prefix, state_table[s->state],
prefix, s->path);
exec_context_dump(&s->exec_context, f, prefix);
for (c = 0; c < _AUTOMOUNT_EXEC_MAX; c++) {
ExecCommand *i;
LIST_FOREACH(i, s->exec_command[c])
fprintf(f, "%s%s: %s\n", prefix, command_table[c], i->path);
}
}
static NameActiveState automount_active_state(Name *n) {
static const NameActiveState table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = NAME_INACTIVE,
[AUTOMOUNT_START_PRE] = NAME_ACTIVATING,
[AUTOMOUNT_START_POST] = NAME_ACTIVATING,
[AUTOMOUNT_WAITING] = NAME_ACTIVE,
[AUTOMOUNT_RUNNING] = NAME_ACTIVE,
[AUTOMOUNT_STOP_PRE] = NAME_DEACTIVATING,
[AUTOMOUNT_STOP_POST] = NAME_DEACTIVATING,
[AUTOMOUNT_MAINTAINANCE] = NAME_INACTIVE,
};
return table[AUTOMOUNT(n)->state];
}
static void automount_free_hook(Name *n) {
Automount *d = AUTOMOUNT(n);
assert(d);
free(d->path);
}
const NameVTable automount_vtable = {
.suffix = ".mount",
.load = automount_load,
.dump = automount_dump,
.start = NULL,
.stop = NULL,
.reload = NULL,
.active_state = automount_active_state,
.free_hook = automount_free_hook
};

46
automount.h Normal file
View file

@ -0,0 +1,46 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef fooautomounthfoo
#define fooautomounthfoo
typedef struct Automount Automount;
#include "name.h"
typedef enum AutomountState {
AUTOMOUNT_DEAD,
AUTOMOUNT_START_PRE,
AUTOMOUNT_START_POST,
AUTOMOUNT_WAITING,
AUTOMOUNT_RUNNING,
AUTOMOUNT_STOP_PRE,
AUTOMOUNT_STOP_POST,
AUTOMOUNT_MAINTAINANCE,
_AUTOMOUNT_STATE_MAX
} AutomountState;
typedef enum AutomountExecCommand {
AUTOMOUNT_EXEC_START_PRE,
AUTOMOUNT_EXEC_START_POST,
AUTOMOUNT_EXEC_STOP_PRE,
AUTOMOUNT_EXEC_STOP_POST,
_AUTOMOUNT_EXEC_MAX
} AutomountExecCommand;
struct Automount {
Meta meta;
AutomountState state;
char *path;
ExecCommand* exec_command[_AUTOMOUNT_EXEC_MAX];
ExecContext exec_context;
pid_t contol_pid;
Mount *mount;
};
extern const NameVTable automount_vtable;
#endif

47
device.c Normal file
View file

@ -0,0 +1,47 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include "name.h"
#include "device.h"
#include "strv.h"
static void device_dump(Name *n, FILE *f, const char *prefix) {
static const char* const state_table[_DEVICE_STATE_MAX] = {
[DEVICE_DEAD] = "dead",
[DEVICE_AVAILABLE] = "available"
};
Device *s = DEVICE(n);
assert(s);
fprintf(f,
"%sDevice State: %s\n",
prefix, state_table[s->state]);
}
static NameActiveState device_active_state(Name *n) {
return DEVICE(n)->state == DEVICE_DEAD ? NAME_INACTIVE : NAME_ACTIVE;
}
static void device_free_hook(Name *n) {
Device *d = DEVICE(n);
assert(d);
strv_free(d->sysfs);
}
const NameVTable device_vtable = {
.suffix = ".device",
.load = name_load_fragment_and_dropin,
.dump = device_dump,
.start = NULL,
.stop = NULL,
.reload = NULL,
.active_state = device_active_state,
.free_hook = device_free_hook
};

29
device.h Normal file
View file

@ -0,0 +1,29 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef foodevicehfoo
#define foodevicehfoo
typedef struct Device Device;
#include "name.h"
/* We simply watch devices, we cannot plug/unplug them. That
* simplifies the state engine greatly */
typedef enum DeviceState {
DEVICE_DEAD,
DEVICE_AVAILABLE,
_DEVICE_STATE_MAX
} DeviceState;
struct Device {
Meta meta;
DeviceState state;
/* A single device can be created by multiple sysfs objects */
char **sysfs;
};
extern const NameVTable device_vtable;
#endif

68
execute.c Normal file
View file

@ -0,0 +1,68 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include <assert.h>
#include "execute.h"
#include "strv.h"
#include "macro.h"
#include "util.h"
int exec_spawn(const ExecCommand *command, const ExecContext *context, pid_t *ret) {
assert(command);
assert(context);
assert(ret);
return 0;
}
void exec_context_free(ExecContext *c) {
unsigned l;
assert(c);
strv_free(c->environment);
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
free(c->rlimit[l]);
free(c->chdir);
free(c->user);
free(c->group);
free(c->supplementary_groups);
}
void exec_command_free_list(ExecCommand *c) {
ExecCommand *i;
while ((i = c)) {
LIST_REMOVE(ExecCommand, c, i);
free(i->path);
free(i->argv);
free(i);
}
}
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
assert(c);
assert(f);
if (!prefix)
prefix = "";
fprintf(f,
"%sUmask: %04o\n"
"%sDumpable: %s\n"
"%sDirectory: %s\n",
prefix, c->umask,
prefix, yes_no(c->dumpable),
prefix, c->chdir ? c->chdir : "/");
}
void exec_context_defaults(ExecContext *c) {
assert(c);
c->umask = 0002;
cap_clear(c->capabilities);
c->dumpable = true;
}

59
execute.h Normal file
View file

@ -0,0 +1,59 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef fooexecutehfoo
#define fooexecutehfoo
typedef struct ExecStatus ExecStatus;
typedef struct ExecCommand ExecCommand;
typedef struct ExecContext ExecContext;
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/capability.h>
#include <stdbool.h>
#include <stdio.h>
#include "list.h"
struct ExecStatus {
pid_t pid;
time_t timestamp;
int status; /* as in wait() */
};
struct ExecCommand {
char *path;
char **argv;
ExecStatus last_exec_status;
LIST_FIELDS(ExecCommand);
};
struct ExecContext {
char **environment;
mode_t umask;
struct rlimit *rlimit[RLIMIT_NLIMITS];
cap_t capabilities;
bool capabilities_set:1;
bool dumpable:1;
int oom_adjust;
int nice;
char *chdir;
/* since resolving these names might might involve socket
* connections and we don't want to deadlock ourselves these
* names are resolved on execution only. */
char *user;
char *group;
char **supplementary_groups;
};
int exec_spawn(const ExecCommand *command, const ExecContext *context, pid_t *ret);
void exec_context_free(ExecContext *c);
void exec_command_free_list(ExecCommand *c);
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
void exec_context_defaults(ExecContext *c);
#endif

257
job.c
View file

@ -30,14 +30,15 @@ void job_free(Job *j) {
assert(j);
/* Detach from next 'bigger' objects */
if (j->linked) {
if (j->name->meta.job == j)
j->name->meta.job = NULL;
hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
j->linked = false;
}
/* Detach from next 'smaller' objects */
manager_transaction_unlink_job(j->manager, j);
free(j);
@ -132,15 +133,14 @@ const char* job_type_to_string(JobType t) {
static const char* const job_type_table[_JOB_TYPE_MAX] = {
[JOB_START] = "start",
[JOB_VERIFY_ACTIVE] = "verify-active",
[JOB_STOP] = "stop",
[JOB_VERIFY_STARTED] = "verify-started",
[JOB_RELOAD] = "reload",
[JOB_RELOAD_OR_START] = "reload-or-start",
[JOB_RESTART] = "restart",
[JOB_TRY_RESTART] = "try-restart",
};
if (t < 0 || t >= _JOB_TYPE_MAX)
return "n/a";
@ -151,8 +151,7 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
static const char* const job_state_table[_JOB_STATE_MAX] = {
[JOB_WAITING] = "waiting",
[JOB_RUNNING] = "running",
[JOB_DONE] = "done"
[JOB_RUNNING] = "running"
};
assert(j);
@ -161,10 +160,12 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
fprintf(f,
"%sJob %u:\n"
"%s\tAction: %s → %s\n"
"%s\tState: %s\n",
"%s\tState: %s\n"
"%s\tForced: %s\n",
prefix, j->id,
prefix, name_id(j->name), job_type_to_string(j->type),
prefix, job_state_table[j->state]);
prefix, job_state_table[j->state],
prefix, yes_no(j->forced));
}
bool job_is_anchor(Job *j) {
@ -198,24 +199,24 @@ int job_type_merge(JobType *a, JobType b) {
/* Also, if a merged with b cannot be merged with c, then
* either a or b cannot be merged with c either */
if (types_match(*a, b, JOB_START, JOB_VERIFY_STARTED))
if (types_match(*a, b, JOB_START, JOB_VERIFY_ACTIVE))
*a = JOB_START;
else if (types_match(*a, b, JOB_START, JOB_RELOAD) ||
types_match(*a, b, JOB_START, JOB_RELOAD_OR_START) ||
types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD_OR_START) ||
types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_RELOAD_OR_START) ||
types_match(*a, b, JOB_RELOAD, JOB_RELOAD_OR_START))
*a = JOB_RELOAD_OR_START;
else if (types_match(*a, b, JOB_START, JOB_RESTART) ||
types_match(*a, b, JOB_START, JOB_TRY_RESTART) ||
types_match(*a, b, JOB_VERIFY_STARTED, JOB_RESTART) ||
types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_RESTART) ||
types_match(*a, b, JOB_RELOAD, JOB_RESTART) ||
types_match(*a, b, JOB_RELOAD_OR_START, JOB_RESTART) ||
types_match(*a, b, JOB_RELOAD_OR_START, JOB_TRY_RESTART) ||
types_match(*a, b, JOB_RESTART, JOB_TRY_RESTART))
*a = JOB_RESTART;
else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD))
else if (types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_RELOAD))
*a = JOB_RELOAD;
else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_TRY_RESTART) ||
else if (types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_TRY_RESTART) ||
types_match(*a, b, JOB_RELOAD, JOB_TRY_RESTART))
*a = JOB_TRY_RESTART;
else
@ -224,40 +225,43 @@ int job_type_merge(JobType *a, JobType b) {
return 0;
}
bool job_type_mergeable(JobType a, JobType b) {
bool job_type_is_mergeable(JobType a, JobType b) {
return job_type_merge(&a, b) >= 0;
}
bool job_type_is_superset(JobType a, JobType b) {
/* Checks whether operation a is a "superset" of b */
/* Checks whether operation a is a "superset" of b in its
* actions */
if (a == b)
return true;
switch (a) {
case JOB_START:
return b == JOB_VERIFY_STARTED;
return b == JOB_VERIFY_ACTIVE;
case JOB_RELOAD:
return b == JOB_VERIFY_STARTED;
return
b == JOB_VERIFY_ACTIVE;
case JOB_RELOAD_OR_START:
return
b == JOB_RELOAD ||
b == JOB_START;
b == JOB_START ||
b == JOB_VERIFY_ACTIVE;
case JOB_RESTART:
return
b == JOB_START ||
b == JOB_VERIFY_STARTED ||
b == JOB_VERIFY_ACTIVE ||
b == JOB_RELOAD ||
b == JOB_RELOAD_OR_START ||
b == JOB_TRY_RESTART;
case JOB_TRY_RESTART:
return
b == JOB_VERIFY_STARTED ||
b == JOB_VERIFY_ACTIVE ||
b == JOB_RELOAD;
default:
return false;
@ -269,30 +273,223 @@ bool job_type_is_conflicting(JobType a, JobType b) {
assert(a >= 0 && a < _JOB_TYPE_MAX);
assert(b >= 0 && b < _JOB_TYPE_MAX);
return
(a == JOB_STOP && b != JOB_STOP) ||
(b == JOB_STOP && a != JOB_STOP);
return (a == JOB_STOP) != (b == JOB_STOP);
}
bool job_type_applicable(JobType j, NameType n) {
bool job_type_is_applicable(JobType j, NameType n) {
assert(j >= 0 && j < _JOB_TYPE_MAX);
assert(n >= 0 && n < _NAME_TYPE_MAX);
switch (j) {
case JOB_VERIFY_ACTIVE:
case JOB_START:
case JOB_STOP:
case JOB_VERIFY_STARTED:
return true;
case JOB_RELOAD:
case JOB_RELOAD_OR_START:
return n == NAME_SERVICE || n == NAME_TIMER || n == NAME_MOUNT;
case JOB_STOP:
case JOB_RESTART:
case JOB_TRY_RESTART:
return n == NAME_SERVICE || n == NAME_TIMER || n == NAME_SOCKET || NAME_MOUNT || NAME_SNAPSHOT;
return name_type_can_start(n);
case JOB_RELOAD:
return name_type_can_reload(n);
case JOB_RELOAD_OR_START:
return name_type_can_reload(n) && name_type_can_start(n);
default:
assert_not_reached("Invalid job type");
}
}
bool job_is_runnable(Job *j) {
void *state;
Name *other;
assert(j);
assert(j->linked);
/* Checks whether there is any job running for the names 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
* . */
if (j->type == JOB_START ||
j->type == JOB_VERIFY_ACTIVE ||
j->type == JOB_RELOAD ||
j->type == JOB_RELOAD_OR_START) {
/* Immediate result is that the job is or might be
* started. In this case lets wait for the
* dependencies, regardless whether they are
* starting or stopping something. */
SET_FOREACH(other, j->name->meta.dependencies[NAME_AFTER], state)
if (other->meta.job)
return false;
}
/* Also, if something else is being stopped and we should
* change state after it, then lets wait. */
SET_FOREACH(other, j->name->meta.dependencies[NAME_BEFORE], state)
if (other->meta.job &&
(other->meta.job->type == JOB_STOP ||
other->meta.job->type == JOB_RESTART ||
other->meta.job->type == JOB_TRY_RESTART))
return false;
/* This means that for a service a and a service b where b
* shall be started after a:
*
* start a + start b 1st step start a, 2nd step start b
* start a + stop b 1st step stop b, 2nd step start a
* stop a + start b 1st step stop a, 2nd step start b
* stop a + stop b 1st step stop b, 2nd step stop a
*
* This has the side effect that restarts are properly
* synchronized too. */
return true;
}
int job_run_and_invalidate(Job *j) {
int r;
assert(j);
if (!job_is_runnable(j))
return -EAGAIN;
if (j->state != JOB_WAITING)
return 0;
switch (j->type) {
case JOB_START:
r = name_start(j->name);
if (r == -EBADR)
r = 0;
break;
case JOB_VERIFY_ACTIVE: {
NameActiveState t = name_active_state(j->name);
if (NAME_IS_ACTIVE_OR_RELOADING(t))
r = -EALREADY;
else if (t == NAME_ACTIVATING)
r = -EAGAIN;
else
r = -ENOEXEC;
break;
}
case JOB_STOP:
r = name_stop(j->name);
break;
case JOB_RELOAD:
r = name_reload(j->name);
break;
case JOB_RELOAD_OR_START:
if (name_active_state(j->name) == NAME_ACTIVE)
r = name_reload(j->name);
else
r = name_start(j->name);
break;
case JOB_RESTART: {
NameActiveState t = name_active_state(j->name);
if (t == NAME_INACTIVE || t == NAME_ACTIVATING) {
j->type = JOB_START;
r = name_start(j->name);
} else
r = name_stop(j->name);
break;
}
case JOB_TRY_RESTART: {
NameActiveState t = name_active_state(j->name);
if (t == NAME_INACTIVE || t == NAME_DEACTIVATING)
r = -ENOEXEC;
else if (t == NAME_ACTIVATING) {
j->type = JOB_START;
r = name_start(j->name);
} else
r = name_stop(j->name);
break;
}
default:
;
}
if (r >= 0)
j->state = JOB_RUNNING;
else if (r == -EALREADY)
r = job_finish_and_invalidate(j, true);
else if (r != -EAGAIN)
r = job_finish_and_invalidate(j, false);
return r;
}
int job_finish_and_invalidate(Job *j, bool success) {
Name *n;
void *state;
Name *other;
NameType t;
assert(j);
if (success && (j->type == JOB_RESTART || j->type == JOB_TRY_RESTART)) {
j->state = JOB_RUNNING;
j->type = JOB_START;
return job_run_and_invalidate(j);
}
n = j->name;
t = j->type;
job_free(j);
/* Fail depending jobs on failure */
if (!success) {
if (t == JOB_START ||
t == JOB_VERIFY_ACTIVE ||
t == JOB_RELOAD_OR_START) {
SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRED_BY], state)
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], state)
if (other->meta.job &&
!other->meta.job->forced &&
(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);
} else if (t == JOB_STOP) {
SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], state)
if (other->meta.job &&
(t == JOB_START ||
t == JOB_VERIFY_ACTIVE ||
t == JOB_RELOAD_OR_START))
job_finish_and_invalidate(other->meta.job, false);
}
}
/* Try to start the next jobs that can be started */
SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], state)
if (other->meta.job)
job_run_and_invalidate(other->meta.job);
SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], state)
if (other->meta.job)
job_run_and_invalidate(other->meta.job);
return 0;
}

31
job.h
View file

@ -9,8 +9,8 @@
typedef struct Job Job;
typedef struct JobDependency JobDependency;
typedef enum JobType JobType;
typedef enum JobMode JobMode;
typedef enum JobState JobState;
typedef enum JobMode JobMode;
#include "manager.h"
#include "name.h"
@ -18,13 +18,20 @@ typedef enum JobState JobState;
#include "list.h"
enum JobType {
JOB_START,
JOB_START, /* if a name does not support being started, we'll just wait until it becomes active */
JOB_VERIFY_ACTIVE,
JOB_STOP,
JOB_VERIFY_STARTED,
JOB_RELOAD, /* reload if running */
JOB_RELOAD_OR_START, /* reload if running, start if not running */
JOB_RESTART, /* stop if running, then start unconditionally */
JOB_TRY_RESTART, /* stop and start if running */
JOB_RELOAD, /* if running reload */
JOB_RELOAD_OR_START, /* if running reload, if not running start */
/* Note that restarts are first treated like JOB_STOP, but
* then instead of finishing are patched to become
* JOB_START. */
JOB_RESTART, /* if running stop, then start unconditionally */
JOB_TRY_RESTART, /* if running stop and then start */
_JOB_TYPE_MAX,
_JOB_TYPE_INVALID = -1
};
@ -32,7 +39,6 @@ enum JobType {
enum JobState {
JOB_WAITING,
JOB_RUNNING,
JOB_DONE,
_JOB_STATE_MAX
};
@ -66,6 +72,7 @@ struct Job {
bool linked:1;
bool matters_to_anchor:1;
bool forced:1;
/* These fields are used only while building a transaction */
Job *transaction_next, *transaction_prev;
@ -73,7 +80,7 @@ struct Job {
JobDependency *subject_list;
JobDependency *object_list;
/* used for graph algs as a "I have been here" marker */
/* Used for graph algs as a "I have been here" marker */
Job* marker;
unsigned generation;
};
@ -92,8 +99,12 @@ int job_merge(Job *j, Job *other);
const char* job_type_to_string(JobType t);
int job_type_merge(JobType *a, JobType b);
bool job_type_mergeable(JobType a, JobType b);
bool job_type_is_mergeable(JobType a, JobType b);
bool job_type_is_superset(JobType a, JobType b);
bool job_type_is_conflicting(JobType a, JobType b);
bool job_type_is_applicable(JobType j, NameType n);
int job_run_and_invalidate(Job *j);
int job_finish_and_invalidate(Job *j, bool success);
#endif

11
load-dropin.c Normal file
View file

@ -0,0 +1,11 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include "load-dropin.h"
int name_load_dropin(Name *n) {
assert(n);
/* Load dependencies from supplementary drop-in directories */
return 0;
}

12
load-dropin.h Normal file
View file

@ -0,0 +1,12 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef fooloaddropinhfoo
#define fooloaddropinhfoo
#include "name.h"
/* Read service data supplementary drop-in directories */
int name_load_dropin(Name *n);
#endif

View file

@ -89,7 +89,7 @@ static int config_parse_names(
if (other != name) {
if (other->meta.state != NAME_STUB) {
if (other->meta.load_state != NAME_STUB) {
free(t);
return -EEXIST;
}
@ -176,7 +176,7 @@ static int config_parse_type(
int name_load_fragment(Name *n) {
const char *const section_table[_NAME_TYPE_MAX] = {
static const char* const section_table[_NAME_TYPE_MAX] = {
[NAME_SERVICE] = "Service",
[NAME_TIMER] = "Timer",
[NAME_SOCKET] = "Socket",
@ -211,7 +211,7 @@ int name_load_fragment(Name *n) {
const char *sections[3];
assert(n);
assert(n->meta.state == NAME_STUB);
assert(n->meta.load_state == NAME_STUB);
sections[0] = "Meta";
sections[1] = section_table[n->meta.type];

11
load-fstab.c Normal file
View file

@ -0,0 +1,11 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include "load-fstab.h"
int name_load_fstab(Name *n) {
assert(n);
/* Load dependencies from /etc/fstab */
return 0;
}

12
load-fstab.h Normal file
View file

@ -0,0 +1,12 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef fooloadfstabhfoo
#define fooloadfstabhfoo
#include "name.h"
/* Read service data from /etc/fstab */
int name_load_fstab(Name *n);
#endif

View file

@ -127,6 +127,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job
j->type = t;
j->state = JOB_WAITING;
j->forced = j->forced || other->forced;
j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
@ -168,7 +169,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job
transaction_delete_job(m, other);
}
static int delete_one_unmergable_job(Manager *m, Job *j) {
static int delete_one_unmergeable_job(Manager *m, Job *j) {
Job *k;
assert(j);
@ -185,7 +186,7 @@ static int delete_one_unmergable_job(Manager *m, Job *j) {
Job *d;
/* Is this one mergeable? Then skip it */
if (job_type_mergeable(j->type, k->type))
if (job_type_is_mergeable(j->type, k->type))
continue;
/* Ok, we found two that conflict, let's see if we can
@ -198,7 +199,7 @@ static int delete_one_unmergable_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", name_id(d->name));
log_debug("Try to fix job merging by deleting job %s/%s", name_id(d->name), job_type_to_string(d->type));
transaction_delete_job(m, d);
return 0;
}
@ -228,7 +229,7 @@ static int transaction_merge_jobs(Manager *m) {
* action. Let's see if we can get rid of one
* of them */
if ((r = delete_one_unmergable_job(m, j)) >= 0)
if ((r = delete_one_unmergeable_job(m, j)) >= 0)
/* Ok, we managed to drop one, now
* let's ask our callers to call us
* again after garbage collecting */
@ -248,7 +249,7 @@ static int transaction_merge_jobs(Manager *m) {
for (k = j->transaction_next; k; k = k->transaction_next)
assert_se(job_type_merge(&t, k->type) == 0);
/* If an active job is mergable, merge it too */
/* 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 */
@ -310,7 +311,7 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned
!name_matters_to_anchor(k->name, k)) {
/* Ok, we can drop this one, so let's
* do so. */
log_debug("Breaking order cycle by deleting job %s", name_id(k->name));
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);
return -EAGAIN;
}
@ -385,8 +386,7 @@ static void transaction_collect_garbage(Manager *m) {
if (j->object_list)
continue;
log_debug("Garbage collecting job %s", name_id(j->name));
log_debug("Garbage collecting job %s/%s", name_id(j->name), job_type_to_string(j->type));
transaction_delete_job(m, j);
again = true;
break;
@ -442,11 +442,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_dead(j->name)) &&
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)))
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));
transaction_delete_job(m, j);
again = true;
break;
@ -551,7 +552,7 @@ static int transaction_activate(Manager *m, JobMode mode) {
}
for (;;) {
/* Fifth step: let's drop unmergable entries if
/* Fifth step: let's drop unmergeable entries if
* necessary and possible, merge entries we can
* merge */
if ((r = transaction_merge_jobs(m)) >= 0)
@ -565,7 +566,7 @@ static int transaction_activate(Manager *m, JobMode mode) {
transaction_collect_garbage(m);
/* Let's see if the resulting transaction still has
* unmergable entries ... */
* unmergeable entries ... */
}
/* Seventh step: check whether we can actually apply this */
@ -587,7 +588,7 @@ rollback:
return r;
}
static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool *is_new) {
static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool force, bool *is_new) {
Job *j, *f;
int r;
@ -628,6 +629,7 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool *
j->generation = 0;
j->marker = NULL;
j->matters_to_anchor = false;
j->forced = force;
if (is_new)
*is_new = true;
@ -660,7 +662,9 @@ void manager_transaction_unlink_job(Manager *m, Job *j) {
job_dependency_free(j->object_list);
if (other) {
log_debug("Deleting job %s as dependency of job %s", name_id(other->name), name_id(j->name));
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));
transaction_delete_job(m, other);
}
}
@ -677,14 +681,14 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Name *
assert(type < _JOB_TYPE_MAX);
assert(name);
if (name->meta.state != NAME_LOADED)
if (name->meta.load_state != NAME_LOADED)
return -EINVAL;
if (!job_type_applicable(type, name->meta.type))
if (!job_type_is_applicable(type, name->meta.type))
return -EBADR;
/* First add the job. */
if (!(ret = transaction_add_one_job(m, type, name, &is_new)))
if (!(ret = transaction_add_one_job(m, type, name, force, &is_new)))
return -ENOMEM;
/* Then, add a link to the job. */
@ -704,10 +708,10 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Name *
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) != -EBADR)
goto fail;
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], state)
if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_STARTED, dep, ret, true, force, NULL)) != -EBADR)
if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) != -EBADR)
goto fail;
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], state)
if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_STARTED, dep, ret, !force, force, NULL)) != -EBADR)
if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) != -EBADR)
goto fail;
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], state)
if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) != -EBADR)

View file

@ -34,6 +34,8 @@ struct Manager {
JobDependency *transaction_anchor;
bool dispatching_load_queue:1;
Hashmap *pids; /* pid => Name object n:1 */
};
Manager* manager_new(void);

32
milestone.c Normal file
View file

@ -0,0 +1,32 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include "name.h"
#include "milestone.h"
#include "load-fragment.h"
static NameActiveState milestone_active_state(Name *n) {
return MILESTONE(n)->state == MILESTONE_DEAD ? NAME_INACTIVE : NAME_ACTIVE;
}
static void milestone_free_hook(Name *n) {
Milestone *m = MILESTONE(n);
assert(m);
/* Nothing here for now */
}
const NameVTable milestone_vtable = {
.suffix = ".milestone",
.load = name_load_fragment,
.dump = NULL,
.start = NULL,
.stop = NULL,
.reload = NULL,
.active_state = milestone_active_state,
.free_hook = milestone_free_hook
};

23
milestone.h Normal file
View file

@ -0,0 +1,23 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef foomilestonehfoo
#define foomilestonehfoo
typedef struct Milestone Milestone;
#include "name.h"
typedef enum MilestoneState {
MILESTONE_DEAD,
MILESTONE_ACTIVE
} MilestoneState;
struct Milestone {
Meta meta;
MilestoneState state;
};
extern const NameVTable milestone_vtable;
#endif

86
mount.c Normal file
View file

@ -0,0 +1,86 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include <errno.h>
#include "name.h"
#include "mount.h"
#include "load-fragment.h"
#include "load-fstab.h"
#include "load-dropin.h"
static int mount_load(Name *n) {
int r;
Mount *m = MOUNT(n);
assert(m);
/* Load a .mount file */
if ((r = name_load_fragment(n)) < 0 && errno != -ENOENT)
return r;
/* Load entry from /etc/fstab */
if ((r = name_load_fstab(n)) < 0)
return r;
/* Load drop-in directory data */
if ((r = name_load_dropin(n)) < 0)
return r;
return r;
}
static void mount_dump(Name *n, FILE *f, const char *prefix) {
static const char* const state_table[_MOUNT_STATE_MAX] = {
[MOUNT_DEAD] = "dead",
[MOUNT_MOUNTING] = "mounting",
[MOUNT_MOUNTED] = "mounted",
[MOUNT_UNMOUNTING] = "unmounting",
[MOUNT_MAINTAINANCE] = "maintainance"
};
Mount *s = MOUNT(n);
assert(s);
fprintf(f,
"%sMount State: %s\n"
"%sPath: %s\n",
prefix, state_table[s->state],
prefix, s->path);
}
static NameActiveState mount_active_state(Name *n) {
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,
};
return table[MOUNT(n)->state];
}
static void mount_free_hook(Name *n) {
Mount *d = MOUNT(n);
assert(d);
free(d->path);
}
const NameVTable mount_vtable = {
.suffix = ".mount",
.load = mount_load,
.dump = mount_dump,
.start = NULL,
.stop = NULL,
.reload = NULL,
.active_state = mount_active_state,
.free_hook = mount_free_hook
};

28
mount.h Normal file
View file

@ -0,0 +1,28 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef foomounthfoo
#define foomounthfoo
typedef struct Mount Mount;
#include "name.h"
typedef enum MountState {
MOUNT_DEAD,
MOUNT_MOUNTING,
MOUNT_MOUNTED,
MOUNT_UNMOUNTING,
MOUNT_MAINTAINANCE,
_MOUNT_STATE_MAX
} MountState;
struct Mount {
Meta meta;
MountState state;
char *path;
};
extern const NameVTable mount_vtable;
#endif

571
name.c
View file

@ -9,24 +9,28 @@
#include "macro.h"
#include "strv.h"
#include "load-fragment.h"
#include "load-dropin.h"
static const NameVTable * const name_vtable[_NAME_TYPE_MAX] = {
[NAME_SERVICE] = &service_vtable,
[NAME_TIMER] = &timer_vtable,
[NAME_SOCKET] = &socket_vtable,
[NAME_MILESTONE] = &milestone_vtable,
[NAME_DEVICE] = &device_vtable,
[NAME_MOUNT] = &mount_vtable,
[NAME_AUTOMOUNT] = &automount_vtable,
[NAME_SNAPSHOT] = &snapshot_vtable
};
#define NAME_VTABLE(n) name_vtable[(n)->meta.type]
NameType name_type_from_string(const char *n) {
NameType t;
static const char* suffixes[_NAME_TYPE_MAX] = {
[NAME_SERVICE] = ".service",
[NAME_TIMER] = ".timer",
[NAME_SOCKET] = ".socket",
[NAME_MILESTONE] = ".milestone",
[NAME_DEVICE] = ".device",
[NAME_MOUNT] = ".mount",
[NAME_AUTOMOUNT] = ".automount",
[NAME_SNAPSHOT] = ".snapshot",
};
assert(n);
for (t = 0; t < _NAME_TYPE_MAX; t++)
if (endswith(n, suffixes[t]))
if (endswith(n, name_vtable[t]->suffix))
return t;
return _NAME_TYPE_INVALID;
@ -44,6 +48,9 @@ bool name_is_valid(const char *n) {
assert(n);
if (strlen(n) >= NAME_MAX)
return false;
t = name_type_from_string(n);
if (t < 0 || t >= _NAME_TYPE_MAX)
return false;
@ -74,7 +81,6 @@ Name *name_new(Manager *m) {
/* Not much initialization happening here at this time */
n->meta.manager = m;
n->meta.type = _NAME_TYPE_INVALID;
n->meta.state = NAME_STUB;
/* We don't link the name here, that is left for name_link() */
@ -130,7 +136,7 @@ int name_link(Name *n) {
return r;
}
if (n->meta.state == NAME_STUB)
if (n->meta.load_state == NAME_STUB)
LIST_PREPEND(Meta, n->meta.manager->load_queue, &n->meta);
return 0;
@ -169,7 +175,7 @@ void name_free(Name *name) {
SET_FOREACH(t, name->meta.names, state)
hashmap_remove_value(name->meta.manager->names, t, name);
if (name->meta.state == NAME_STUB)
if (name->meta.load_state == NAME_STUB)
LIST_REMOVE(Meta, name->meta.manager->load_queue, &name->meta);
}
@ -180,41 +186,8 @@ void name_free(Name *name) {
for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
bidi_set_free(name, name->meta.dependencies[d]);
switch (name->meta.type) {
case NAME_SOCKET: {
unsigned i;
Socket *s = SOCKET(name);
for (i = 0; i < s->n_fds; i++)
close_nointr(s->fds[i]);
break;
}
case NAME_DEVICE: {
Device *d = DEVICE(name);
free(d->sysfs);
break;
}
case NAME_MOUNT: {
Mount *m = MOUNT(name);
free(m->path);
break;
}
case NAME_AUTOMOUNT: {
Automount *a = AUTOMOUNT(name);
free(a->path);
break;
}
default:
;
}
if (NAME_VTABLE(name)->free_hook)
NAME_VTABLE(name)->free_hook(name);
free(name->meta.description);
@ -225,134 +198,15 @@ void name_free(Name *name) {
free(name);
}
bool name_is_ready(Name *name) {
NameActiveState name_active_state(Name *name) {
assert(name);
if (name->meta.state != NAME_LOADED)
return false;
if (name->meta.load_state != NAME_LOADED)
return NAME_INACTIVE;
assert(name->meta.type < _NAME_TYPE_MAX);
switch (name->meta.type) {
case NAME_SERVICE: {
Service *s = SERVICE(name);
return
s->state == SERVICE_RUNNING ||
s->state == SERVICE_RELOAD_PRE ||
s->state == SERVICE_RELOAD ||
s->state == SERVICE_RELOAD_POST;
}
case NAME_TIMER: {
Timer *t = TIMER(name);
return
t->state == TIMER_WAITING ||
t->state == TIMER_RUNNING;
}
case NAME_SOCKET: {
Socket *s = SOCKET(name);
return
s->state == SOCKET_LISTENING ||
s->state == SOCKET_RUNNING;
}
case NAME_MILESTONE:
return MILESTONE(name)->state == MILESTONE_ACTIVE;
case NAME_DEVICE:
return DEVICE(name)->state == DEVICE_AVAILABLE;
case NAME_MOUNT:
return MOUNT(name)->state == MOUNT_MOUNTED;
case NAME_AUTOMOUNT: {
Automount *a = AUTOMOUNT(name);
return
a->state == AUTOMOUNT_WAITING ||
a->state == AUTOMOUNT_RUNNING;
}
case NAME_SNAPSHOT:
return SNAPSHOT(name)->state == SNAPSHOT_ACTIVE;
case _NAME_TYPE_MAX:
case _NAME_TYPE_INVALID:
;
}
assert_not_reached("Unknown name type.");
return false;
return NAME_VTABLE(name)->active_state(name);
}
bool name_is_dead(Name *name) {
assert(name);
if (name->meta.state != NAME_LOADED)
return true;
assert(name->meta.type < _NAME_TYPE_MAX);
switch (name->meta.type) {
case NAME_SERVICE: {
Service *s = SERVICE(name);
return
s->state == SERVICE_DEAD ||
s->state == SERVICE_MAINTAINANCE;
}
case NAME_TIMER:
return TIMER(name)->state == TIMER_DEAD;
case NAME_SOCKET: {
Socket *s = SOCKET(name);
return
s->state == SOCKET_DEAD ||
s->state == SOCKET_MAINTAINANCE;
}
case NAME_MILESTONE:
return MILESTONE(name)->state == MILESTONE_DEAD;
case NAME_DEVICE:
return DEVICE(name)->state == DEVICE_DEAD;
case NAME_MOUNT: {
Mount *a = MOUNT(name);
return
a->state == AUTOMOUNT_DEAD ||
a->state == AUTOMOUNT_MAINTAINANCE;
}
case NAME_AUTOMOUNT: {
Automount *a = AUTOMOUNT(name);
return
a->state == AUTOMOUNT_DEAD ||
a->state == AUTOMOUNT_MAINTAINANCE;
}
case NAME_SNAPSHOT:
return SNAPSHOT(name)->state == SNAPSHOT_DEAD;
case _NAME_TYPE_MAX:
case _NAME_TYPE_INVALID:
;
}
assert_not_reached("Unknown name type.");
return false;
}
static int ensure_in_set(Set **s, void *data) {
int r;
@ -399,7 +253,7 @@ int name_merge(Name *name, Name *other) {
if (name->meta.type != other->meta.type)
return -EINVAL;
if (other->meta.state != NAME_STUB)
if (other->meta.load_state != NAME_STUB)
return -EINVAL;
/* Merge names */
@ -452,13 +306,11 @@ static int augment(Name *n) {
if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n)) < 0)
return r;
SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], state)
if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n)) < 0)
return r;
SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], state)
if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n)) < 0)
if ((r = ensure_in_set(&other->meta.dependencies[NAME_SOFT_REQUIRED_BY], n)) < 0)
return r;
SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUISITE], state)
SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], state)
if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n)) < 0)
return r;
@ -483,26 +335,28 @@ const char* name_id(Name *n) {
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 state_table[_NAME_STATE_MAX] = {
static const char* const load_state_table[_NAME_LOAD_STATE_MAX] = {
[NAME_STUB] = "stub",
[NAME_LOADED] = "loaded",
[NAME_FAILED] = "failed"
};
static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = "dead",
[SOCKET_BEFORE] = "before",
[SOCKET_START_PRE] = "start-pre",
[SOCKET_START] = "start",
[SOCKET_START_POST] = "start-post",
[SOCKET_LISTENING] = "listening",
[SOCKET_RUNNING] = "running",
[SOCKET_STOP_PRE] = "stop-pre",
[SOCKET_STOP] = "stop",
[SOCKET_STOP_POST] = "stop-post",
[SOCKET_MAINTAINANCE] = "maintainance"
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] = {
@ -512,7 +366,7 @@ void name_dump(Name *n, FILE *f, const char *prefix) {
[NAME_REQUISITE] = "Requisite",
[NAME_SOFT_REQUISITE] = "SoftRequisite",
[NAME_REQUIRED_BY] = "RequiredBy",
[NAME_WANTED_BY] = "WantedBy",
[NAME_SOFT_REQUIRED_BY] = "SoftRequiredBy",
[NAME_CONFLICTS] = "Conflicts",
[NAME_BEFORE] = "Before",
[NAME_AFTER] = "After",
@ -530,15 +384,15 @@ void name_dump(Name *n, FILE *f, const char *prefix) {
fprintf(f,
"%sName %s:\n"
"%s\tDescription: %s\n"
"%s\tName State: %s\n",
"%s\tName Load State: %s\n"
"%s\tName Active State: %s\n",
prefix, name_id(n),
prefix, n->meta.description ? n->meta.description : name_id(n),
prefix, state_table[n->meta.state]);
prefix, name_description(n),
prefix, load_state_table[n->meta.load_state],
prefix, active_state_table[name_active_state(n)]);
fprintf(f, "%s\tNames: ", prefix);
SET_FOREACH(t, n->meta.names, state)
fprintf(f, "%s ", t);
fprintf(f, "\n");
fprintf(f, "%s\tName: %s\n", prefix, t);
for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) {
void *state;
@ -547,39 +401,12 @@ void name_dump(Name *n, FILE *f, const char *prefix) {
if (set_isempty(n->meta.dependencies[d]))
continue;
fprintf(f, "%s\t%s: ", prefix, dependency_table[d]);
SET_FOREACH(other, n->meta.dependencies[d], state)
fprintf(f, "%s ", name_id(other));
fprintf(f, "\n");
fprintf(f, "%s\t%s: %s\n", prefix, dependency_table[d], name_id(other));
}
switch (n->meta.type) {
case NAME_SOCKET: {
int r;
char *s = NULL;
const char *t;
if ((r = address_print(&n->socket.address, &s)) < 0)
t = strerror(-r);
else
t = s;
fprintf(f,
"%s\tAddress: %s\n"
"%s\tSocket State: %s\n",
prefix, t,
prefix, socket_state_table[n->socket.state]);
free(s);
break;
}
default:
;
}
if (NAME_VTABLE(n)->dump)
NAME_VTABLE(n)->dump(n, f, prefix);
if (n->meta.job) {
char *p;
@ -623,36 +450,19 @@ static int verify_type(Name *name) {
return 0;
}
static int service_load_sysv(Service *s) {
assert(s);
/* Common implementation for multiple backends */
int name_load_fragment_and_dropin(Name *n) {
int r;
/* Load service data from SysV init scripts, preferably with
* LSB headers ... */
return -ENOENT;
}
static int name_load_fstab(Name *n) {
assert(n);
assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT);
/* Load mount data from /etc/fstab */
return 0;
}
static int snapshot_load(Snapshot *s) {
assert(s);
/* Load snapshots from disk */
return 0;
}
static int name_load_dropin(Name *n) {
assert(n);
/* Load dependencies from drop-in directories */
/* 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;
}
@ -662,52 +472,229 @@ int name_load(Name *name) {
assert(name);
if (name->meta.state != NAME_STUB)
if (name->meta.load_state != NAME_STUB)
return 0;
if ((r = verify_type(name)) < 0)
return r;
if (name->meta.type == NAME_SERVICE) {
/* Load a .service file */
if ((r = name_load_fragment(name)) == 0)
goto finish;
/* Load a classic init script */
if (r == -ENOENT)
if ((r = service_load_sysv(SERVICE(name))) == 0)
goto finish;
} else if (name->meta.type == NAME_MOUNT ||
name->meta.type == NAME_AUTOMOUNT) {
if ((r = name_load_fstab(name)) == 0)
goto finish;
} else if (name->meta.type == NAME_SNAPSHOT) {
if ((r = snapshot_load(SNAPSHOT(name))) == 0)
goto finish;
} else {
if ((r = name_load_fragment(name)) == 0)
goto finish;
}
name->meta.state = NAME_FAILED;
return r;
finish:
if ((r = name_load_dropin(name)) < 0)
return r;
if (NAME_VTABLE(name)->load)
if ((r = NAME_VTABLE(name)->load(name)) < 0)
goto fail;
if ((r = name_sanitize(name)) < 0)
return r;
goto fail;
if ((r = name_link_names(name, false)) < 0)
return r;
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;
if (state == NAME_ACTIVATING)
return 0;
return NAME_VTABLE(n)->start(n);
}
bool name_type_can_start(NameType t) {
assert(t >= 0 && t < _NAME_TYPE_MAX);
return !!name_vtable[t]->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_VTABLE(n)->reload)
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_type_can_reload(NameType t) {
assert(t >= 0 && t < _NAME_TYPE_MAX);
return !!name_vtable[t]->reload;
}
static void retroactively_start_dependencies(Name *n) {
void *state;
Name *other;
assert(n);
assert(NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(n)));
SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], state)
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], state)
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], state)
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], state)
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], state)
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) {
void *state;
Name *other;
assert(n);
assert(NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(n)));
SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRED_BY], state)
if (!NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(other)))
manager_add_job(n->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL);
}
int 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 0;
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_run_and_invalidate(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))
return job_finish_and_invalidate(n->meta.job, true);
else if (ns == NAME_ACTIVATING)
return 0;
else
job_finish_and_invalidate(n->meta.job, false);
break;
case JOB_RELOAD:
case JOB_RELOAD_OR_START:
if (ns == NAME_ACTIVE)
return job_finish_and_invalidate(n->meta.job, true);
else if (ns == NAME_ACTIVATING || ns == NAME_ACTIVE_RELOADING)
return 0;
else
job_finish_and_invalidate(n->meta.job, false);
break;
case JOB_STOP:
case JOB_RESTART:
case JOB_TRY_RESTART:
if (ns == NAME_INACTIVE)
return job_finish_and_invalidate(n->meta.job, true);
else if (ns == NAME_DEACTIVATING)
return 0;
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);
name->meta.state = NAME_LOADED;
return 0;
}

284
name.h
View file

@ -8,14 +8,11 @@
typedef union Name Name;
typedef struct Meta Meta;
typedef struct Service Service;
typedef struct Timer Timer;
typedef struct Socket Socket;
typedef struct Milestone Milestone;
typedef struct Device Device;
typedef struct Mount Mount;
typedef struct Automount Automount;
typedef struct Snapshot Snapshot;
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"
@ -23,8 +20,11 @@ typedef struct Snapshot Snapshot;
#include "util.h"
#include "list.h"
#include "socket-util.h"
#include "execute.h"
typedef enum NameType {
#define NAME_MAX 32
enum NameType {
NAME_SERVICE = 0,
NAME_TIMER,
NAME_SOCKET,
@ -35,38 +35,60 @@ typedef enum NameType {
NAME_SNAPSHOT,
_NAME_TYPE_MAX,
_NAME_TYPE_INVALID = -1,
} NameType;
};
typedef enum NameState {
enum NameLoadState {
NAME_STUB,
NAME_LOADED,
NAME_FAILED,
_NAME_STATE_MAX
} NameState;
_NAME_LOAD_STATE_MAX
};
typedef enum NameDependency {
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,
NAME_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */
NAME_WANTED_BY, /* inverse of 'wants', 'soft_requires' and 'soft_requisite' is 'wanted_by' */
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' */
NAME_CONFLICTS, /* inverse of 'conflicts' is 'conflicts' */
/* Order */
NAME_BEFORE, /* inverse of before is after and vice versa */
NAME_BEFORE, /* inverse of before is after and vice versa */
NAME_AFTER,
_NAME_DEPENDENCY_MAX
} NameDependency;
};
struct Meta {
Manager *manager;
NameType type;
NameState state;
NameLoadState load_state;
Set *names;
Set *dependencies[_NAME_DEPENDENCY_MAX];
@ -83,164 +105,14 @@ struct Meta {
LIST_FIELDS(Meta);
};
typedef enum ServiceState {
SERVICE_DEAD,
SERVICE_BEFORE,
SERVICE_START_PRE,
SERVICE_START,
SERVICE_START_POST,
SERVICE_RUNNING,
SERVICE_RELOAD_PRE,
SERVICE_RELOAD,
SERVICE_RELOAD_POST,
SERVICE_STOP_PRE,
SERVICE_STOP,
SERVICE_SIGTERM,
SERVICE_SIGKILL,
SERVICE_STOP_POST,
SERVICE_HOLDOFF,
SERVICE_MAINTAINANCE
} ServiceState;
typedef enum ServiceMode {
SERVICE_ONCE,
SERVICE_RESTART
} ServiceMode;
struct Service {
Meta meta;
ServiceState state;
ServiceMode mode;
};
typedef enum TimerState {
TIMER_DEAD,
TIMER_BEFORE,
TIMER_START_PRE,
TIMER_START,
TIMER_START_POST,
TIMER_WAITING,
TIMER_RUNNING,
TIMER_STOP_PRE,
TIMER_STOP,
TIMER_STOP_POST
} TimerState;
struct Timer {
Meta meta;
TimerState state;
Service *subject;
clockid_t clock_id;
usec_t next_elapse;
};
typedef enum SocketState {
SOCKET_DEAD,
SOCKET_BEFORE,
SOCKET_START_PRE,
SOCKET_START,
SOCKET_START_POST,
SOCKET_LISTENING,
SOCKET_RUNNING,
SOCKET_STOP_PRE,
SOCKET_STOP,
SOCKET_STOP_POST,
SOCKET_MAINTAINANCE,
_SOCKET_STATE_MAX
} SocketState;
struct Socket {
Meta meta;
SocketState state;
Address address;
int *fds;
unsigned n_fds;
Service *subject;
};
typedef enum MilestoneState {
MILESTONE_DEAD,
MILESTONE_BEFORE,
MILESTONE_ACTIVE
} MilestoneState;
struct Milestone {
Meta meta;
MilestoneState state;
};
typedef enum DeviceState {
DEVICE_DEAD,
DEVICE_BEFORE,
DEVICE_AVAILABLE
} DeviceState;
struct Device {
Meta meta;
DeviceState state;
char *sysfs;
};
typedef enum MountState {
MOUNT_DEAD,
MOUNT_BEFORE,
MOUNT_MOUNTING,
MOUNT_MOUNTED,
MOUNT_UNMOUNTING,
MOUNT_SIGTERM, /* if the mount command hangs */
MOUNT_SIGKILL,
MOUNT_MAINTAINANCE
} MountState;
struct Mount {
Meta meta;
MountState state;
char *path;
};
typedef enum AutomountState {
AUTOMOUNT_DEAD,
AUTOMOUNT_BEFORE,
AUTOMOUNT_START_PRE,
AUTOMOUNT_START,
AUTOMOUNT_START_POST,
AUTOMOUNT_WAITING,
AUTOMOUNT_RUNNING,
AUTOMOUNT_STOP_PRE,
AUTOMOUNT_STOP,
AUTOMOUNT_STOP_POST,
AUTOMOUNT_MAINTAINANCE
} AutomountState;
struct Automount {
Meta meta;
AutomountState state;
char *path;
Mount *subject;
};
typedef enum SnapshotState {
SNAPSHOT_DEAD,
SNAPSHOT_BEFORE,
SNAPSHOT_ACTIVE
} SnapshotState;
struct Snapshot {
Meta meta;
SnapshotState state;
bool cleanup:1;
};
#include "service.h"
#include "timer.h"
#include "socket.h"
#include "milestone.h"
#include "device.h"
#include "mount.h"
#include "automount.h"
#include "snapshot.h"
union Name {
Meta meta;
@ -254,30 +126,48 @@ union Name {
Snapshot snapshot;
};
/* For casting a name into the various name types */
struct NameVTable {
const char *suffix;
#define DEFINE_CAST(UPPERCASE, MixedCase, lowercase) \
int (*load)(Name *n);
void (*dump)(Name *n, FILE *f, const char *prefix);
int (*start)(Name *n);
int (*stop)(Name *n);
int (*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 (*free_hook)(Name *n);
};
/* For casting a name into the various name types */
#define DEFINE_CAST(UPPERCASE, MixedCase) \
static inline MixedCase* UPPERCASE(Name *name) { \
if (name->meta.type != NAME_##UPPERCASE) \
if (!name || name->meta.type != NAME_##UPPERCASE) \
return NULL; \
\
return &name->lowercase; \
return (MixedCase*) name; \
}
DEFINE_CAST(SERVICE, Service, service);
DEFINE_CAST(TIMER, Timer, timer);
DEFINE_CAST(SOCKET, Socket, socket);
DEFINE_CAST(MILESTONE, Milestone, milestone);
DEFINE_CAST(DEVICE, Device, device);
DEFINE_CAST(MOUNT, Mount, mount);
DEFINE_CAST(AUTOMOUNT, Automount, automount);
DEFINE_CAST(SNAPSHOT, Snapshot, snapshot);
/* For casting the various name types into a name */
#define NAME(o) ((Name*) (o))
bool name_is_running(Name *name);
bool name_is_dead(Name *name);
DEFINE_CAST(SOCKET, Socket);
DEFINE_CAST(TIMER, Timer);
DEFINE_CAST(SERVICE, Service);
DEFINE_CAST(MILESTONE, Milestone);
DEFINE_CAST(DEVICE, Device);
DEFINE_CAST(MOUNT, Mount);
DEFINE_CAST(AUTOMOUNT, Automount);
DEFINE_CAST(SNAPSHOT, Snapshot);
NameActiveState name_active_state(Name *name);
bool name_type_can_start(NameType t);
bool name_type_can_reload(NameType t);
NameType name_type_from_string(const char *n);
bool name_is_valid(const char *n);
@ -288,9 +178,17 @@ int name_link(Name *name);
int name_link_names(Name *name, bool replace);
int name_merge(Name *name, Name *other);
int name_sanitize(Name *n);
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);
void name_dump(Name *n, FILE *f, const char *prefix);
int name_start(Name *n);
int name_stop(Name *n);
int name_reload(Name *n);
int name_notify(Name *n, NameActiveState old, NameActiveState new);
#endif

182
service.c Normal file
View file

@ -0,0 +1,182 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include <errno.h>
#include "name.h"
#include "service.h"
#include "load-fragment.h"
#include "load-dropin.h"
static int service_load_sysv(Service *s) {
assert(s);
/* Load service data from SysV init scripts, preferably with
* LSB headers ... */
return -ENOENT;
}
static int service_load(Name *n) {
int r;
Service *s = SERVICE(n);
assert(s);
exec_context_defaults(&s->exec_context);
/* Load a .service file */
r = name_load_fragment(n);
/* Load a classic init script as a fallback */
if (r == -ENOENT)
r = service_load_sysv(s);
if (r < 0)
return r;
/* Load dropin directory data */
if ((r = name_load_dropin(n)) < 0)
return r;
return 0;
}
static void service_dump(Name *n, FILE *f, const char *prefix) {
static const char* const state_table[_SERVICE_STATE_MAX] = {
[SERVICE_DEAD] = "dead",
[SERVICE_START_PRE] = "start-pre",
[SERVICE_START] = "start",
[SERVICE_START_POST] = "post",
[SERVICE_RUNNING] = "running",
[SERVICE_RELOAD_PRE] = "reload-pre",
[SERVICE_RELOAD] = "reload",
[SERVICE_RELOAD_POST] = "reload-post",
[SERVICE_STOP_PRE] = "stop-pre",
[SERVICE_STOP] = "stop",
[SERVICE_SIGTERM] = "sigterm",
[SERVICE_SIGKILL] = "sigkill",
[SERVICE_STOP_POST] = "stop-post",
[SERVICE_MAINTAINANCE] = "maintainance"
};
static const char* const command_table[_SERVICE_EXEC_MAX] = {
[SERVICE_EXEC_START_PRE] = "StartPre",
[SERVICE_EXEC_START] = "Start",
[SERVICE_EXEC_START_POST] = "StartPost",
[SERVICE_EXEC_RELOAD_PRE] = "ReloadPre",
[SERVICE_EXEC_RELOAD] = "Reload",
[SERVICE_EXEC_RELOAD_POST] = "ReloadPost",
[SERVICE_EXEC_STOP_PRE] = "StopPre",
[SERVICE_EXEC_STOP] = "Stop",
[SERVICE_EXEC_STOP_POST] = "StopPost",
};
ServiceExecCommand c;
Service *s = SERVICE(n);
assert(s);
fprintf(f,
"%sService State: %s\n",
prefix, state_table[s->state]);
exec_context_dump(&s->exec_context, f, prefix);
for (c = 0; c < _SERVICE_EXEC_MAX; c++) {
ExecCommand *i;
LIST_FOREACH(i, s->exec_command[c])
fprintf(f, "%s%s: %s\n", prefix, command_table[c], i->path);
}
}
static int service_set_state(Service *s, ServiceState state) {
assert(s);
s->state = state;
return 0;
}
static int service_start(Name *n) {
Service *s = SERVICE(n);
assert(s);
/* We cannot fulfill this request right now */
if (s->state == SERVICE_STOP_PRE ||
s->state == SERVICE_STOP ||
s->state == SERVICE_SIGTERM ||
s->state == SERVICE_SIGKILL ||
s->state == SERVICE_STOP_POST)
return -EAGAIN;
assert(s->state == SERVICE_DEAD || s->state == SERVICE_MAINTAINANCE);
return service_set_state(s, SERVICE_START_PRE);
}
static int service_stop(Name *n) {
Service *s = SERVICE(n);
assert(s);
return 0;
}
static int service_reload(Name *n) {
return 0;
}
static NameActiveState service_active_state(Name *n) {
static const NameActiveState 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_PRE] = NAME_ACTIVE_RELOADING,
[SERVICE_RELOAD] = NAME_ACTIVE_RELOADING,
[SERVICE_RELOAD_POST] = NAME_ACTIVE_RELOADING,
[SERVICE_STOP_PRE] = NAME_DEACTIVATING,
[SERVICE_STOP] = NAME_DEACTIVATING,
[SERVICE_SIGTERM] = NAME_DEACTIVATING,
[SERVICE_SIGKILL] = NAME_DEACTIVATING,
[SERVICE_STOP_POST] = NAME_DEACTIVATING,
[SERVICE_MAINTAINANCE] = NAME_INACTIVE,
};
return table[SERVICE(n)->state];
}
static void service_free_hook(Name *n) {
Service *s = SERVICE(n);
unsigned c;
assert(s);
exec_context_free(&s->exec_context);
for (c = 0; c < _SERVICE_EXEC_MAX; c++)
exec_command_free_list(s->exec_command[c]);
if (s->socket)
s->socket->service = NULL;
}
const NameVTable service_vtable = {
.suffix = ".service",
.load = service_load,
.dump = service_dump,
.start = service_start,
.stop = service_stop,
.reload = service_reload,
.active_state = service_active_state,
.free_hook = service_free_hook
};

65
service.h Normal file
View file

@ -0,0 +1,65 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef fooservicehfoo
#define fooservicehfoo
typedef struct Service Service;
#include "name.h"
#include "socket.h"
#include "timer.h"
typedef enum ServiceState {
SERVICE_DEAD,
SERVICE_START_PRE,
SERVICE_START,
SERVICE_START_POST,
SERVICE_RUNNING,
SERVICE_RELOAD_PRE,
SERVICE_RELOAD,
SERVICE_RELOAD_POST,
SERVICE_STOP_PRE,
SERVICE_STOP,
SERVICE_SIGTERM,
SERVICE_SIGKILL,
SERVICE_STOP_POST,
SERVICE_MAINTAINANCE,
_SERVICE_STATE_MAX,
} ServiceState;
typedef enum ServiceMode {
SERVICE_ONCE,
SERVICE_RESTART
} ServiceMode;
typedef enum ServiceExecCommand {
SERVICE_EXEC_START_PRE,
SERVICE_EXEC_START,
SERVICE_EXEC_START_POST,
SERVICE_EXEC_RELOAD_PRE,
SERVICE_EXEC_RELOAD,
SERVICE_EXEC_RELOAD_POST,
SERVICE_EXEC_STOP_PRE,
SERVICE_EXEC_STOP,
SERVICE_EXEC_STOP_POST,
_SERVICE_EXEC_MAX
} ServiceExecCommand;
struct Service {
Meta meta;
ServiceState state;
ServiceMode mode;
ExecCommand* exec_command[_SERVICE_EXEC_MAX];
ExecContext exec_context;
pid_t service_pid, control_pid;
Socket *socket;
Timer *timer;
};
const NameVTable service_vtable;
#endif

31
snapshot.c Normal file
View file

@ -0,0 +1,31 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include "name.h"
#include "snapshot.h"
static NameActiveState snapshot_active_state(Name *n) {
return SNAPSHOT(n)->state == SNAPSHOT_DEAD ? NAME_INACTIVE : NAME_ACTIVE;
}
static void snapshot_free_hook(Name *n) {
Snapshot *s = SNAPSHOT(n);
assert(s);
/* Nothing here for now */
}
const NameVTable snapshot_vtable = {
.suffix = ".snapshot",
.load = NULL,
.dump = NULL,
.start = NULL,
.stop = NULL,
.reload = NULL,
.active_state = snapshot_active_state,
.free_hook = snapshot_free_hook
};

24
snapshot.h Normal file
View file

@ -0,0 +1,24 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef foosnapshothfoo
#define foosnapshothfoo
typedef struct Snapshot Snapshot;
#include "name.h"
typedef enum SnapshotState {
SNAPSHOT_DEAD,
SNAPSHOT_ACTIVE
} SnapshotState;
struct Snapshot {
Meta meta;
SnapshotState state;
bool cleanup:1;
};
extern const NameVTable snapshot_vtable;
#endif

113
socket.c Normal file
View file

@ -0,0 +1,113 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include "name.h"
#include "socket.h"
static int socket_load(Name *n) {
Socket *s = SOCKET(n);
exec_context_defaults(&s->exec_context);
return name_load_fragment_and_dropin(n);
}
static void socket_dump(Name *n, FILE *f, const char *prefix) {
static const char* const state_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = "dead",
[SOCKET_START_PRE] = "start-pre",
[SOCKET_START_POST] = "start-post",
[SOCKET_LISTENING] = "listening",
[SOCKET_RUNNING] = "running",
[SOCKET_STOP_PRE] = "stop-pre",
[SOCKET_STOP_POST] = "stop-post",
[SOCKET_MAINTAINANCE] = "maintainance"
};
static const char* const command_table[_SOCKET_EXEC_MAX] = {
[SOCKET_EXEC_START_PRE] = "StartPre",
[SOCKET_EXEC_START_POST] = "StartPost",
[SOCKET_EXEC_STOP_PRE] = "StopPre",
[SOCKET_EXEC_STOP_POST] = "StopPost"
};
SocketExecCommand c;
Socket *s = SOCKET(n);
const char *t;
int r;
char *k;
assert(s);
if ((r = address_print(&n->socket.address, &k)) < 0)
t = strerror(-r);
else
t = k;
fprintf(f,
"%sSocket State: %s\n"
"%sAddress: %s\n",
prefix, state_table[s->state],
prefix, t);
free(k);
exec_context_dump(&s->exec_context, f, prefix);
for (c = 0; c < _SOCKET_EXEC_MAX; c++) {
ExecCommand *i;
LIST_FOREACH(i, s->exec_command[c])
fprintf(f, "%s%s: %s\n", prefix, command_table[c], i->path);
}
}
static NameActiveState socket_active_state(Name *n) {
static const NameActiveState 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_POST] = NAME_DEACTIVATING,
[SOCKET_MAINTAINANCE] = NAME_INACTIVE,
};
return table[SOCKET(n)->state];
}
static void socket_free_hook(Name *n) {
unsigned i;
SocketExecCommand c;
Socket *s = SOCKET(n);
assert(s);
for (i = 0; i < s->n_fds; i++)
close_nointr(s->fds[i]);
exec_context_free(&s->exec_context);
for (c = 0; c < _SOCKET_EXEC_MAX; c++)
exec_command_free_list(s->exec_command[c]);
if (s->service)
s->service->socket = NULL;
}
const NameVTable socket_vtable = {
.suffix = ".socket",
.load = socket_load,
.dump = socket_dump,
.start = NULL,
.stop = NULL,
.reload = NULL,
.active_state = socket_active_state,
.free_hook = socket_free_hook
};

49
socket.h Normal file
View file

@ -0,0 +1,49 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef foosockethfoo
#define foosockethfoo
typedef struct Socket Socket;
#include "name.h"
typedef enum SocketState {
SOCKET_DEAD,
SOCKET_START_PRE,
SOCKET_START_POST,
SOCKET_LISTENING,
SOCKET_RUNNING,
SOCKET_STOP_PRE,
SOCKET_STOP_POST,
SOCKET_MAINTAINANCE,
_SOCKET_STATE_MAX
} SocketState;
typedef enum SocketExecCommand {
SOCKET_EXEC_START_PRE,
SOCKET_EXEC_START_POST,
SOCKET_EXEC_STOP_PRE,
SOCKET_EXEC_STOP_POST,
_SOCKET_EXEC_MAX
} SocketExecCommand;
struct Socket {
Meta meta;
SocketState state;
Address address;
int *fds;
unsigned n_fds;
ExecCommand* exec_command[_SOCKET_EXEC_MAX];
ExecContext exec_context;
pid_t control_pid;
Service *service;
};
extern const NameVTable socket_vtable;
#endif

View file

@ -13,16 +13,16 @@ int main(int argc, char*argv[]) {
for (a = 0; a < _JOB_TYPE_MAX; a++)
for (b = 0; b < _JOB_TYPE_MAX; b++) {
if (!job_type_mergeable(a, b))
if (!job_type_is_mergeable(a, b))
printf("Not mergeable: %s + %s\n", job_type_to_string(a), job_type_to_string(b));
for (c = 0; c < _JOB_TYPE_MAX; c++) {
/* Verify transitivity of mergeability
* of job types */
assert(!job_type_mergeable(a, b) ||
!job_type_mergeable(b, c) ||
job_type_mergeable(a, c));
assert(!job_type_is_mergeable(a, b) ||
!job_type_is_mergeable(b, c) ||
job_type_is_mergeable(a, c));
d = a;
if (job_type_merge(&d, b) >= 0) {
@ -32,14 +32,14 @@ int main(int argc, char*argv[]) {
/* Verify that merged entries can be
* merged with the same entries they
* can be merged with seperately */
assert(!job_type_mergeable(a, c) || job_type_mergeable(d, c));
assert(!job_type_mergeable(b, c) || job_type_mergeable(d, c));
assert(!job_type_is_mergeable(a, c) || job_type_is_mergeable(d, c));
assert(!job_type_is_mergeable(b, c) || job_type_is_mergeable(d, c));
/* Verify that if a merged
* with b is not mergable with
* c then either a or b is not
* mergeable with c either. */
assert(job_type_mergeable(d, c) || !job_type_mergeable(a, c) || !job_type_mergeable(b, c));
assert(job_type_is_mergeable(d, c) || !job_type_is_mergeable(a, c) || !job_type_is_mergeable(b, c));
e = b;
if (job_type_merge(&e, c) >= 0) {

3
test2/h.service Normal file
View file

@ -0,0 +1,3 @@
[Meta]
Description=H
Wants=g.service

39
timer.c Normal file
View file

@ -0,0 +1,39 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#include "name.h"
#include "timer.h"
static NameActiveState timer_active_state(Name *n) {
static const NameActiveState table[_TIMER_STATE_MAX] = {
[TIMER_DEAD] = NAME_INACTIVE,
[TIMER_WAITING] = NAME_ACTIVE,
[TIMER_RUNNING] = NAME_ACTIVE
};
return table[TIMER(n)->state];
}
static void timer_free_hook(Name *n) {
Timer *t = TIMER(n);
assert(t);
if (t->service)
t->service->timer = NULL;
}
const NameVTable timer_vtable = {
.suffix = ".timer",
.load = name_load_fragment_and_dropin,
.dump = NULL,
.start = NULL,
.stop = NULL,
.reload = NULL,
.active_state = timer_active_state,
.free_hook = timer_free_hook
};

30
timer.h Normal file
View file

@ -0,0 +1,30 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef footimerhfoo
#define footimerhfoo
typedef struct Timer Timer;
#include "name.h"
typedef enum TimerState {
TIMER_DEAD,
TIMER_WAITING,
TIMER_RUNNING,
_TIMER_STATE_MAX
} TimerState;
struct Timer {
Meta meta;
TimerState state;
clockid_t clock_id;
usec_t next_elapse;
Service *service;
};
const NameVTable timer_vtable;
#endif

1
util.h
View file

@ -7,6 +7,7 @@
#include <time.h>
#include <sys/time.h>
#include <stdbool.h>
#include <stdlib.h>
typedef uint64_t usec_t;