mirror of
https://github.com/AuxXxilium/eudev.git
synced 2025-01-18 10:07:46 +07:00
first attempt in implementinging execution logic
This commit is contained in:
parent
cd2dbd7df9
commit
5cb5a6ffc3
26
Makefile
26
Makefile
@ -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
111
automount.c
Normal 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
46
automount.h
Normal 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
47
device.c
Normal 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
29
device.h
Normal 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
68
execute.c
Normal 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
59
execute.h
Normal 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
257
job.c
@ -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
31
job.h
@ -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
11
load-dropin.c
Normal 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
12
load-dropin.h
Normal 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
|
@ -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
11
load-fstab.c
Normal 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
12
load-fstab.h
Normal 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
|
40
manager.c
40
manager.c
@ -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)
|
||||
|
@ -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
32
milestone.c
Normal 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
23
milestone.h
Normal 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
86
mount.c
Normal 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
28
mount.h
Normal 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
571
name.c
@ -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
284
name.h
@ -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
182
service.c
Normal 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
65
service.h
Normal 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
31
snapshot.c
Normal 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
24
snapshot.h
Normal 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
113
socket.c
Normal 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
49
socket.h
Normal 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
|
@ -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
3
test2/h.service
Normal file
@ -0,0 +1,3 @@
|
||||
[Meta]
|
||||
Description=H
|
||||
Wants=g.service
|
39
timer.c
Normal file
39
timer.c
Normal 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
30
timer.h
Normal 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
|
Loading…
Reference in New Issue
Block a user