chiark / gitweb /
first attempt in implementinging execution logic
authorLennart Poettering <lennart@poettering.net>
Sat, 23 Jan 2010 00:52:57 +0000 (01:52 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 23 Jan 2010 00:52:57 +0000 (01:52 +0100)
33 files changed:
Makefile
automount.c [new file with mode: 0644]
automount.h [new file with mode: 0644]
device.c [new file with mode: 0644]
device.h [new file with mode: 0644]
execute.c [new file with mode: 0644]
execute.h [new file with mode: 0644]
job.c
job.h
load-dropin.c [new file with mode: 0644]
load-dropin.h [new file with mode: 0644]
load-fragment.c
load-fstab.c [new file with mode: 0644]
load-fstab.h [new file with mode: 0644]
manager.c
manager.h
milestone.c [new file with mode: 0644]
milestone.h [new file with mode: 0644]
mount.c [new file with mode: 0644]
mount.h [new file with mode: 0644]
name.c
name.h
service.c [new file with mode: 0644]
service.h [new file with mode: 0644]
snapshot.c [new file with mode: 0644]
snapshot.h [new file with mode: 0644]
socket.c [new file with mode: 0644]
socket.h [new file with mode: 0644]
test-job-type.c
test2/h.service [new file with mode: 0644]
timer.c [new file with mode: 0644]
timer.h [new file with mode: 0644]
util.h

index 5c6d798..ac815a1 100644 (file)
--- a/Makefile
+++ b/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
 
diff --git a/automount.c b/automount.c
new file mode 100644 (file)
index 0000000..84691e6
--- /dev/null
@@ -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
+};
diff --git a/automount.h b/automount.h
new file mode 100644 (file)
index 0000000..b6dfd5b
--- /dev/null
@@ -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
diff --git a/device.c b/device.c
new file mode 100644 (file)
index 0000000..79847c4
--- /dev/null
+++ b/device.c
@@ -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
+};
diff --git a/device.h b/device.h
new file mode 100644 (file)
index 0000000..e2597f7
--- /dev/null
+++ b/device.h
@@ -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
diff --git a/execute.c b/execute.c
new file mode 100644 (file)
index 0000000..9bb8351
--- /dev/null
+++ b/execute.c
@@ -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;
+}
diff --git a/execute.h b/execute.h
new file mode 100644 (file)
index 0000000..fb952d1
--- /dev/null
+++ b/execute.h
@@ -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
diff --git a/job.c b/job.c
index 4688164..0ae1a76 100644 (file)
--- a/job.c
+++ b/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;
+}
diff --git a/job.h b/job.h
index d839db5..fbfaa16 100644 (file)
--- a/job.h
+++ b/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
diff --git a/load-dropin.c b/load-dropin.c
new file mode 100644 (file)
index 0000000..7105cb8
--- /dev/null
@@ -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;
+}
diff --git a/load-dropin.h b/load-dropin.h
new file mode 100644 (file)
index 0000000..c4971a5
--- /dev/null
@@ -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
index ed046d0..a2c3636 100644 (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];
diff --git a/load-fstab.c b/load-fstab.c
new file mode 100644 (file)
index 0000000..cfefcb6
--- /dev/null
@@ -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;
+}
diff --git a/load-fstab.h b/load-fstab.h
new file mode 100644 (file)
index 0000000..e705650
--- /dev/null
@@ -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
index 14ae652..701e645 100644 (file)
--- a/manager.c
+++ b/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)
index 287e532..e669f9f 100644 (file)
--- a/manager.h
+++ b/manager.h
@@ -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);
diff --git a/milestone.c b/milestone.c
new file mode 100644 (file)
index 0000000..ad8080a
--- /dev/null
@@ -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
+};
diff --git a/milestone.h b/milestone.h
new file mode 100644 (file)
index 0000000..332fbe4
--- /dev/null
@@ -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
diff --git a/mount.c b/mount.c
new file mode 100644 (file)
index 0000000..fa92e5b
--- /dev/null
+++ b/mount.c
@@ -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
+};
diff --git a/mount.h b/mount.h
new file mode 100644 (file)
index 0000000..b437b2e
--- /dev/null
+++ b/mount.h
@@ -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
diff --git a/name.c b/name.c
index 08194d0..5dba010 100644 (file)
--- a/name.c
+++ b/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;
-
-        assert(name->meta.type < _NAME_TYPE_MAX);
+        if (name->meta.load_state != NAME_LOADED)
+                return NAME_INACTIVE;
 
-        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,91 +450,251 @@ 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 ... */
+        assert(n);
 
-        return -ENOENT;
+        /* 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;
 }
 
-static int name_load_fstab(Name *n) {
-        assert(n);
-        assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT);
+int name_load(Name *name) {
+        int r;
+
+        assert(name);
+
+        if (name->meta.load_state != NAME_STUB)
+                return 0;
 
-        /* Load mount data from /etc/fstab */
+        if ((r = verify_type(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)
+                goto fail;
+
+        if ((r = name_link_names(name, false)) < 0)
+                goto fail;
+
+        name->meta.load_state = NAME_LOADED;
         return 0;
+
+fail:
+        name->meta.load_state = NAME_FAILED;
+        return r;
 }
 
-static int snapshot_load(Snapshot *s) {
-        assert(s);
+/* 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);
 
-        /* Load snapshots from disk */
+        if (!NAME_VTABLE(n)->start)
+                return -EBADR;
 
-        return 0;
+        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);
 }
 
-static int name_load_dropin(Name *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);
 
-        /* Load dependencies from drop-in directories */
+        if (!NAME_VTABLE(n)->stop)
+                return -EBADR;
 
-        return 0;
+        state = name_active_state(n);
+        if (state == NAME_INACTIVE)
+                return -EALREADY;
+
+        if (state == NAME_DEACTIVATING)
+                return 0;
+
+        return NAME_VTABLE(n)->stop(n);
 }
 
-int name_load(Name *name) {
-        int r;
+/* 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(name);
+        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);
+}
 
-        if (name->meta.state != NAME_STUB)
+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 ((r = verify_type(name)) < 0)
-                return r;
+        if (n->meta.job) {
 
-        if (name->meta.type == NAME_SERVICE) {
+                if (n->meta.job->state == JOB_WAITING)
 
-                /* Load a .service file */
-                if ((r = name_load_fragment(name)) == 0)
-                        goto finish;
+                        /* 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);
 
-                /* Load a classic init script */
-                if (r == -ENOENT)
-                        if ((r = service_load_sysv(SERVICE(name))) == 0)
-                                goto finish;
+                else {
+                        assert(n->meta.job->state == JOB_RUNNING);
 
-        } else if (name->meta.type == NAME_MOUNT ||
-                   name->meta.type == NAME_AUTOMOUNT) {
+                        /* Let's check of this state change
+                         * constitutes a finished job, or maybe
+                         * cotradicts a running job and hence needs to
+                         * invalidate jobs. */
 
-                if ((r = name_load_fstab(name)) == 0)
-                        goto finish;
+                        switch (n->meta.job->type) {
 
-        } else if (name->meta.type == NAME_SNAPSHOT) {
+                                case JOB_START:
+                                case JOB_VERIFY_ACTIVE:
 
-                if ((r = snapshot_load(SNAPSHOT(name))) == 0)
-                        goto finish;
+                                        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);
 
-        } else {
-                if ((r = name_load_fragment(name)) == 0)
-                        goto finish;
-        }
+                                        break;
 
-        name->meta.state = NAME_FAILED;
-        return r;
+                                case JOB_RELOAD:
+                                case JOB_RELOAD_OR_START:
 
-finish:
-        if ((r = name_load_dropin(name)) < 0)
-                return r;
+                                        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);
 
-        if ((r = name_sanitize(name)) < 0)
-                return r;
+                                        break;
 
-        if ((r = name_link_names(name, false)) < 0)
-                return r;
+                                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;
 }
diff --git a/name.h b/name.h
index 869e71b..33baea1 100644 (file)
--- a/name.h
+++ b/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
+};
+
+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;
+}
 
-typedef enum NameDependency {
+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;
+
+        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);
+};
 
-#define DEFINE_CAST(UPPERCASE, MixedCase, lowercase)                    \
+/* 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
diff --git a/service.c b/service.c
new file mode 100644 (file)
index 0000000..ac9c39c
--- /dev/null
+++ b/service.c
@@ -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
+};
diff --git a/service.h b/service.h
new file mode 100644 (file)
index 0000000..d7f2c70
--- /dev/null
+++ b/service.h
@@ -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
diff --git a/snapshot.c b/snapshot.c
new file mode 100644 (file)
index 0000000..fc7bb5f
--- /dev/null
@@ -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
+};
diff --git a/snapshot.h b/snapshot.h
new file mode 100644 (file)
index 0000000..8d35e17
--- /dev/null
@@ -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
diff --git a/socket.c b/socket.c
new file mode 100644 (file)
index 0000000..9f87184
--- /dev/null
+++ b/socket.c
@@ -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
+};
diff --git a/socket.h b/socket.h
new file mode 100644 (file)
index 0000000..fcf09dd
--- /dev/null
+++ b/socket.h
@@ -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
index db5c329..62f04fd 100644 (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) {
diff --git a/test2/h.service b/test2/h.service
new file mode 100644 (file)
index 0000000..4b9ffa3
--- /dev/null
@@ -0,0 +1,3 @@
+[Meta]
+Description=H
+Wants=g.service
diff --git a/timer.c b/timer.c
new file mode 100644 (file)
index 0000000..b1571ce
--- /dev/null
+++ b/timer.c
@@ -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
+};
diff --git a/timer.h b/timer.h
new file mode 100644 (file)
index 0000000..fcbddc0
--- /dev/null
+++ b/timer.h
@@ -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
diff --git a/util.h b/util.h
index f5aaf70..6731414 100644 (file)
--- a/util.h
+++ b/util.h
@@ -7,6 +7,7 @@
 #include <time.h>
 #include <sys/time.h>
 #include <stdbool.h>
+#include <stdlib.h>
 
 typedef uint64_t usec_t;