chiark / gitweb /
greatly extend what we enforce as process properties
authorLennart Poettering <lennart@poettering.net>
Sat, 30 Jan 2010 00:55:42 +0000 (01:55 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 30 Jan 2010 00:55:42 +0000 (01:55 +0100)
13 files changed:
execute.c
execute.h
fixme
job.c
job.h
load-fragment.c
manager.c
service.c
service.h
socket.c
test1/systemd-logger.service
unit.c
unit.h

index f2f2be7..6f12c27 100644 (file)
--- a/execute.c
+++ b/execute.c
@@ -9,6 +9,8 @@
 #include <signal.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <signal.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/prctl.h>
+#include <sched.h>
 
 #include "execute.h"
 #include "strv.h"
 
 #include "execute.h"
 #include "strv.h"
@@ -16,6 +18,7 @@
 #include "util.h"
 #include "log.h"
 #include "ioprio.h"
 #include "util.h"
 #include "log.h"
 #include "ioprio.h"
+#include "securebits.h"
 
 static int close_fds(int except[], unsigned n_except) {
         DIR *d;
 
 static int close_fds(int except[], unsigned n_except) {
         DIR *d;
@@ -166,20 +169,19 @@ static int setup_output(const ExecContext *context, const char *ident) {
 
         switch (context->output) {
 
 
         switch (context->output) {
 
-        case EXEC_CONSOLE:
+        case EXEC_OUTPUT_CONSOLE:
                 return 0;
 
                 return 0;
 
-        case EXEC_NULL:
+        case EXEC_OUTPUT_NULL:
 
 
-                if ((r = replace_null_fd(STDIN_FILENO, O_RDONLY)) < 0 ||
-                    (r = replace_null_fd(STDOUT_FILENO, O_WRONLY)) < 0 ||
+                if ((r = replace_null_fd(STDOUT_FILENO, O_WRONLY)) < 0 ||
                     (r = replace_null_fd(STDERR_FILENO, O_WRONLY)) < 0)
                         return r;
 
                 return 0;
 
                     (r = replace_null_fd(STDERR_FILENO, O_WRONLY)) < 0)
                         return r;
 
                 return 0;
 
-        case EXEC_KERNEL:
-        case EXEC_SYSLOG: {
+        case EXEC_OUTPUT_KERNEL:
+        case EXEC_OUTPUT_SYSLOG: {
 
                 int fd;
                 union {
 
                 int fd;
                 union {
@@ -187,9 +189,6 @@ static int setup_output(const ExecContext *context, const char *ident) {
                         struct sockaddr_un un;
                 } sa;
 
                         struct sockaddr_un un;
                 } sa;
 
-                if ((r = replace_null_fd(STDIN_FILENO, O_RDONLY)) < 0)
-                        return r;
-
                 close_nointr(STDOUT_FILENO);
                 close_nointr(STDERR_FILENO);
 
                 close_nointr(STDOUT_FILENO);
                 close_nointr(STDERR_FILENO);
 
@@ -237,15 +236,37 @@ static int setup_output(const ExecContext *context, const char *ident) {
                         "%s\n"
                         "%i\n"
                         "%s\n",
                         "%s\n"
                         "%i\n"
                         "%s\n",
-                        context->output == EXEC_KERNEL ? "kmsg" : "syslog",
+                        context->output == EXEC_OUTPUT_KERNEL ? "kmsg" : "syslog",
                         context->syslog_priority,
                         context->syslog_identifier ? context->syslog_identifier : ident);
 
                 return 0;
         }
                         context->syslog_priority,
                         context->syslog_identifier ? context->syslog_identifier : ident);
 
                 return 0;
         }
+
+        default:
+                assert_not_reached("Unknown output type");
         }
         }
+}
+
+int setup_input(const ExecContext *context) {
+        int r;
+
+        assert(context);
 
 
-        assert_not_reached("Unknown logging type");
+        switch (context->input) {
+
+        case EXEC_INPUT_CONSOLE:
+                return 0;
+
+        case EXEC_INPUT_NULL:
+                if ((r = replace_null_fd(STDIN_FILENO, O_RDONLY)) < 0)
+                        return r;
+
+                return 0;
+
+        default:
+                assert_not_reached("Unknown input type");
+        }
 }
 
 int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, unsigned n_fds, pid_t *ret) {
 }
 
 int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, unsigned n_fds, pid_t *ret) {
@@ -281,6 +302,11 @@ int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds,
 
                 umask(context->umask);
 
 
                 umask(context->umask);
 
+                if (setup_input(context) < 0) {
+                        r = EXIT_INPUT;
+                        goto fail;
+                }
+
                 if (setup_output(context, file_name_from_path(command->path)) < 0) {
                         r = EXIT_OUTPUT;
                         goto fail;
                 if (setup_output(context, file_name_from_path(command->path)) < 0) {
                         r = EXIT_OUTPUT;
                         goto fail;
@@ -315,12 +341,36 @@ int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds,
                                 goto fail;
                         }
 
                                 goto fail;
                         }
 
+                if (context->cpu_sched_set) {
+                        struct sched_param param;
+
+                        zero(param);
+                        param.sched_priority = context->cpu_sched_priority;
+
+                        if (sched_setscheduler(0, context->cpu_sched_policy, &param) < 0) {
+                                r = EXIT_SETSCHEDULER;
+                                goto fail;
+                        }
+                }
+
+                if (context->cpu_affinity_set)
+                        if (sched_setaffinity(0, sizeof(context->cpu_affinity), &context->cpu_affinity) < 0) {
+                                r = EXIT_CPUAFFINITY;
+                                goto fail;
+                        }
+
                 if (context->ioprio_set)
                         if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
                                 r = EXIT_IOPRIO;
                                 goto fail;
                         }
 
                 if (context->ioprio_set)
                         if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
                                 r = EXIT_IOPRIO;
                                 goto fail;
                         }
 
+                if (context->timer_slack_ns_set)
+                        if (prctl(PR_SET_TIMERSLACK, context->timer_slack_ns_set) < 0) {
+                                r = EXIT_TIMERSLACK;
+                                goto fail;
+                        }
+
                 if (close_fds(fds, n_fds) < 0 ||
                     shift_fds(fds, n_fds) < 0 ||
                     flags_fds(fds, n_fds) < 0) {
                 if (close_fds(fds, n_fds) < 0 ||
                     shift_fds(fds, n_fds) < 0 ||
                     flags_fds(fds, n_fds) < 0) {
@@ -338,6 +388,13 @@ int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds,
                         }
                 }
 
                         }
                 }
 
+                if (context->secure_bits) {
+                        if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
+                                r = EXIT_SECUREBITS;
+                                goto fail;
+                        }
+                }
+
                 if (n_fds > 0) {
                         char a[64], b[64];
                         char *listen_env[3] = {
                 if (n_fds > 0) {
                         char a[64], b[64];
                         char *listen_env[3] = {
@@ -383,17 +440,24 @@ void exec_context_init(ExecContext *c) {
         assert(c);
 
         c->umask = 0002;
         assert(c);
 
         c->umask = 0002;
-        cap_clear(c->capabilities);
-        c->capabilities_set = false;
         c->oom_adjust = 0;
         c->oom_adjust_set = false;
         c->nice = 0;
         c->nice_set = false;
         c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
         c->ioprio_set = false;
         c->oom_adjust = 0;
         c->oom_adjust_set = false;
         c->nice = 0;
         c->nice_set = false;
         c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
         c->ioprio_set = false;
+        c->cpu_sched_policy = SCHED_OTHER;
+        c->cpu_sched_priority = 0;
+        c->cpu_sched_set = false;
+        CPU_ZERO(&c->cpu_affinity);
+        c->cpu_affinity_set = false;
 
 
+        c->input = 0;
         c->output = 0;
         c->syslog_priority = LOG_DAEMON|LOG_INFO;
         c->output = 0;
         c->syslog_priority = LOG_DAEMON|LOG_INFO;
+
+        c->secure_bits = 0;
+        c->capability_bounding_set_drop = 0;
 }
 
 void exec_context_done(ExecContext *c) {
 }
 
 void exec_context_done(ExecContext *c) {
@@ -425,6 +489,11 @@ void exec_context_done(ExecContext *c) {
 
         strv_free(c->supplementary_groups);
         c->supplementary_groups = NULL;
 
         strv_free(c->supplementary_groups);
         c->supplementary_groups = NULL;
+
+        if (c->capabilities) {
+                cap_free(c->capabilities);
+                c->capabilities = NULL;
+        }
 }
 
 void exec_command_free_list(ExecCommand *c) {
 }
 
 void exec_command_free_list(ExecCommand *c) {
@@ -449,13 +518,8 @@ void exec_command_free_array(ExecCommand **c, unsigned n) {
 }
 
 void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
 }
 
 void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
-
-        static const char * const table[] = {
-                [IOPRIO_CLASS_NONE] = "none",
-                [IOPRIO_CLASS_RT] = "realtime",
-                [IOPRIO_CLASS_BE] = "best-effort",
-                [IOPRIO_CLASS_IDLE] = "idle"
-        };
+        char ** e;
+        unsigned i;
 
         assert(c);
         assert(f);
 
         assert(c);
         assert(f);
@@ -464,13 +528,17 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 prefix = "";
 
         fprintf(f,
                 prefix = "";
 
         fprintf(f,
-                "%sUmask: %04o\n"
-                "%sWorking Directory: %s\n"
-                "%sRoot Directory: %s\n",
+                "%sUMask: %04o\n"
+                "%sWorkingDirectory: %s\n"
+                "%sRootDirectory: %s\n",
                 prefix, c->umask,
                 prefix, c->working_directory ? c->working_directory : "/",
                 prefix, c->root_directory ? c->root_directory : "/");
 
                 prefix, c->umask,
                 prefix, c->working_directory ? c->working_directory : "/",
                 prefix, c->root_directory ? c->root_directory : "/");
 
+        if (c->environment)
+                for (e = c->environment; *e; e++)
+                        fprintf(f, "%sEnvironment: %s\n", prefix, *e);
+
         if (c->nice_set)
                 fprintf(f,
                         "%sNice: %i\n",
         if (c->nice_set)
                 fprintf(f,
                         "%sNice: %i\n",
@@ -481,12 +549,98 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                         "%sOOMAdjust: %i\n",
                         prefix, c->oom_adjust);
 
                         "%sOOMAdjust: %i\n",
                         prefix, c->oom_adjust);
 
+        for (i = 0; i < RLIM_NLIMITS; i++)
+                if (c->rlimit[i])
+                        fprintf(f, "%s: %llu\n", rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max);
+
         if (c->ioprio_set)
                 fprintf(f,
                         "%sIOSchedulingClass: %s\n"
                         "%sIOPriority: %i\n",
         if (c->ioprio_set)
                 fprintf(f,
                         "%sIOSchedulingClass: %s\n"
                         "%sIOPriority: %i\n",
-                        prefix, table[IOPRIO_PRIO_CLASS(c->ioprio)],
+                        prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(c->ioprio)),
                         prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
                         prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
+
+        if (c->cpu_sched_set)
+                fprintf(f,
+                        "%sCPUSchedulingPolicy: %s\n"
+                        "%sCPUSchedulingPriority: %i\n",
+                        prefix, sched_policy_to_string(c->cpu_sched_policy),
+                        prefix, c->cpu_sched_priority);
+
+        if (c->cpu_affinity_set) {
+                fprintf(f, "%sCPUAffinity:", prefix);
+                for (i = 0; i < CPU_SETSIZE; i++)
+                        if (CPU_ISSET(i, &c->cpu_affinity))
+                                fprintf(f, " %i", i);
+                fputs("\n", f);
+        }
+
+        if (c->timer_slack_ns_set)
+                fprintf(f, "%sTimerSlackNS: %lu\n", prefix, c->timer_slack_ns);
+
+        fprintf(f,
+                "%sInput: %s\n"
+                "%sOutput: %s\n",
+                prefix, exec_input_to_string(c->input),
+                prefix, exec_output_to_string(c->output));
+
+        if (c->output == EXEC_OUTPUT_SYSLOG || c->output == EXEC_OUTPUT_KERNEL)
+                fprintf(f,
+                        "%sSyslogFacility: %s\n"
+                        "%sSyslogLevel: %s\n",
+                        prefix, log_facility_to_string(LOG_FAC(c->syslog_priority)),
+                        prefix, log_level_to_string(LOG_PRI(c->syslog_priority)));
+
+        if (c->capabilities) {
+                char *t;
+                if ((t = cap_to_text(c->capabilities, NULL))) {
+                        fprintf(f, "%sCapabilities: %s\n",
+                                prefix, t);
+                        cap_free(t);
+                }
+        }
+
+        if (c->secure_bits)
+                fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
+                        prefix,
+                        (c->secure_bits & SECURE_KEEP_CAPS) ? " keep-caps" : "",
+                        (c->secure_bits & SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
+                        (c->secure_bits & SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
+                        (c->secure_bits & SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
+                        (c->secure_bits & SECURE_NOROOT) ? " noroot" : "",
+                        (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
+
+        if (c->capability_bounding_set_drop) {
+                fprintf(f, "%sCapabilityBoundingSetDrop:", prefix);
+
+                for (i = 0; i <= CAP_LAST_CAP; i++)
+                        if (c->capability_bounding_set_drop & (1 << i)) {
+                                char *t;
+
+                                if ((t = cap_to_name(i))) {
+                                        fprintf(f, " %s", t);
+                                        free(t);
+                                }
+                        }
+
+                fputs("\n", f);
+        }
+
+        if (c->user)
+                fprintf(f, "%sUser: %s", prefix, c->user);
+        if (c->group)
+                fprintf(f, "%sGroup: %s", prefix, c->group);
+
+        if (c->supplementary_groups) {
+                char **g;
+
+                fprintf(f, "%sSupplementaryGroups:", prefix);
+
+                STRV_FOREACH(g, c->supplementary_groups)
+                        fprintf(f, " %s", *g);
+
+                fputs("\n", f);
+        }
 }
 
 void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status) {
 }
 
 void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status) {
@@ -565,3 +719,19 @@ void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
         LIST_FOREACH(command, c, c)
                 exec_command_dump(c, f, prefix);
 }
         LIST_FOREACH(command, c, c)
                 exec_command_dump(c, f, prefix);
 }
+
+static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
+        [EXEC_OUTPUT_CONSOLE] = "console",
+        [EXEC_OUTPUT_NULL] = "null",
+        [EXEC_OUTPUT_SYSLOG] = "syslog",
+        [EXEC_OUTPUT_KERNEL] = "kernel"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
+
+static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
+        [EXEC_INPUT_NULL] = "null",
+        [EXEC_INPUT_CONSOLE] = "console"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
index 581736d..fff54b1 100644 (file)
--- a/execute.h
+++ b/execute.h
@@ -12,6 +12,7 @@ typedef struct ExecContext ExecContext;
 #include <sys/capability.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <sys/capability.h>
 #include <stdbool.h>
 #include <stdio.h>
+#include <sched.h>
 
 #include "list.h"
 #include "util.h"
 
 #include "list.h"
 #include "util.h"
@@ -20,12 +21,21 @@ typedef struct ExecContext ExecContext;
 #define LOGGER_SOCKET "/systemd/logger"
 
 typedef enum ExecOutput {
 #define LOGGER_SOCKET "/systemd/logger"
 
 typedef enum ExecOutput {
-        EXEC_CONSOLE,
-        EXEC_NULL,
-        EXEC_SYSLOG,
-        EXEC_KERNEL
+        EXEC_OUTPUT_CONSOLE,
+        EXEC_OUTPUT_NULL,
+        EXEC_OUTPUT_SYSLOG,
+        EXEC_OUTPUT_KERNEL,
+        _EXEC_OUTPUT_MAX,
+        _EXEC_OUTPUT_INVALID = -1
 } ExecOutput;
 
 } ExecOutput;
 
+typedef enum ExecInput {
+        EXEC_INPUT_NULL,
+        EXEC_INPUT_CONSOLE,
+        _EXEC_INPUT_MAX,
+        _EXEC_INPUT_INVALID = -1
+} ExecInput;
+
 struct ExecStatus {
         pid_t pid;
         usec_t timestamp;
 struct ExecStatus {
         pid_t pid;
         usec_t timestamp;
@@ -43,27 +53,37 @@ struct ExecCommand {
 struct ExecContext {
         char **environment;
         mode_t umask;
 struct ExecContext {
         char **environment;
         mode_t umask;
-        struct rlimit *rlimit[RLIMIT_NLIMITS];  /* FIXME: load-fragment parser missing */
+        struct rlimit *rlimit[RLIMIT_NLIMITS];
         char *working_directory, *root_directory;
         int oom_adjust;
         int nice;
         int ioprio;
         char *working_directory, *root_directory;
         int oom_adjust;
         int nice;
         int ioprio;
+        int cpu_sched_policy;
+        int cpu_sched_priority;
+        cpu_set_t cpu_affinity;
+        unsigned long timer_slack_ns;
 
         bool oom_adjust_set:1;
         bool nice_set:1;
         bool ioprio_set:1;
 
         bool oom_adjust_set:1;
         bool nice_set:1;
         bool ioprio_set:1;
+        bool cpu_sched_set:1;
+        bool cpu_affinity_set:1;
+        bool timer_slack_ns_set:1;
 
 
+        ExecInput input;
         ExecOutput output;
         int syslog_priority;
         char *syslog_identifier;
 
         ExecOutput output;
         int syslog_priority;
         char *syslog_identifier;
 
-         /* FIXME: all privs related settings need parser and enforcer */
+        /* FIXME: all privs related settings need to be enforced */
         cap_t capabilities;
         cap_t capabilities;
-        bool capabilities_set:1;
+        int secure_bits;
+        uint64_t capability_bounding_set_drop;
 
 
-        /* since resolving these names might might involve socket
+        /* Since resolving these names might might involve socket
          * connections and we don't want to deadlock ourselves these
          * connections and we don't want to deadlock ourselves these
-         * names are resolved on execution only. */
+         * names are resolved on execution only and in the child
+         * process. */
         char *user;
         char *group;
         char **supplementary_groups;
         char *user;
         char *group;
         char **supplementary_groups;
@@ -82,6 +102,7 @@ typedef enum ExitStatus {
         /* The LSB suggests that error codes >= 200 are "reserved". We
          * use them here under the assumption that they hence are
          * unused by init scripts.
         /* The LSB suggests that error codes >= 200 are "reserved". We
          * use them here under the assumption that they hence are
          * unused by init scripts.
+         * c->
          *
          * http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
 
          *
          * http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
 
@@ -93,10 +114,15 @@ typedef enum ExitStatus {
         EXIT_LIMITS,
         EXIT_OOM_ADJUST,
         EXIT_SIGNAL_MASK,
         EXIT_LIMITS,
         EXIT_OOM_ADJUST,
         EXIT_SIGNAL_MASK,
+        EXIT_INPUT,
         EXIT_OUTPUT,
         EXIT_CHROOT,
         EXIT_PGID,
         EXIT_OUTPUT,
         EXIT_CHROOT,
         EXIT_PGID,
-        EXIT_IOPRIO
+        EXIT_IOPRIO,
+        EXIT_TIMERSLACK,
+        EXIT_SECUREBITS,
+        EXIT_SETSCHEDULER,
+        EXIT_CPUAFFINITY
 } ExitStatus;
 
 int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, unsigned n_fds, pid_t *ret);
 } ExitStatus;
 
 int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, unsigned n_fds, pid_t *ret);
@@ -114,4 +140,10 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
 
 void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status);
 
 
 void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status);
 
+const char* exec_output_to_string(ExecOutput i);
+int exec_output_from_string(const char *s);
+
+const char* exec_input_to_string(ExecInput i);
+int exec_input_from_string(const char *s);
+
 #endif
 #endif
diff --git a/fixme b/fixme
index 2668bbb..2cce6ac 100644 (file)
--- a/fixme
+++ b/fixme
@@ -52,3 +52,5 @@
 
 - ability to kill services? i.e. in contrast to stopping them, go directly
   into killing mode?
 
 - ability to kill services? i.e. in contrast to stopping them, go directly
   into killing mode?
+
+- restart-on-success, restart-on-failure, restart-on-abort
diff --git a/job.c b/job.c
index 148c745..378a85d 100644 (file)
--- a/job.c
+++ b/job.c
@@ -3,8 +3,12 @@
 #include <assert.h>
 #include <errno.h>
 
 #include <assert.h>
 #include <errno.h>
 
+#include "set.h"
+#include "unit.h"
 #include "macro.h"
 #include "macro.h"
-#include "job.h"
+#include "strv.h"
+#include "load-fragment.h"
+#include "load-dropin.h"
 #include "log.h"
 
 Job* job_new(Manager *m, JobType type, Unit *unit) {
 #include "log.h"
 
 Job* job_new(Manager *m, JobType type, Unit *unit) {
@@ -109,30 +113,8 @@ void job_dependency_delete(Job *subject, Job *object, bool *matters) {
         job_dependency_free(l);
 }
 
         job_dependency_free(l);
 }
 
-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_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";
-
-        return job_type_table[t];
-}
-
 void job_dump(Job *j, FILE*f, const char *prefix) {
 
 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"
-        };
 
         assert(j);
         assert(f);
 
         assert(j);
         assert(f);
@@ -144,7 +126,7 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
                 "%s\tForced: %s\n",
                 prefix, j->id,
                 prefix, unit_id(j->unit), job_type_to_string(j->type),
                 "%s\tForced: %s\n",
                 prefix, j->id,
                 prefix, unit_id(j->unit), job_type_to_string(j->type),
-                prefix, job_state_table[j->state],
+                prefix, job_state_to_string(j->state),
                 prefix, yes_no(j->forced));
 }
 
                 prefix, yes_no(j->forced));
 }
 
@@ -480,3 +462,22 @@ void job_schedule_run(Job *j) {
         LIST_PREPEND(Job, run_queue, j->manager->run_queue, j);
         j->in_run_queue = true;
 }
         LIST_PREPEND(Job, run_queue, j->manager->run_queue, j);
         j->in_run_queue = true;
 }
+
+static const char* const job_state_table[_JOB_STATE_MAX] = {
+        [JOB_WAITING] = "waiting",
+        [JOB_RUNNING] = "running"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
+
+static const char* const job_type_table[_JOB_TYPE_MAX] = {
+        [JOB_START] = "start",
+        [JOB_VERIFY_ACTIVE] = "verify-active",
+        [JOB_STOP] = "stop",
+        [JOB_RELOAD] = "reload",
+        [JOB_RELOAD_OR_START] = "reload-or-start",
+        [JOB_RESTART] = "restart",
+        [JOB_TRY_RESTART] = "try-restart",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
diff --git a/job.h b/job.h
index dd5e31a..f1a588c 100644 (file)
--- a/job.h
+++ b/job.h
@@ -39,7 +39,8 @@ enum JobType {
 enum JobState {
         JOB_WAITING,
         JOB_RUNNING,
 enum JobState {
         JOB_WAITING,
         JOB_RUNNING,
-        _JOB_STATE_MAX
+        _JOB_STATE_MAX,
+        _JOB_STATE_INVALID = -1
 };
 
 enum JobMode {
 };
 
 enum JobMode {
@@ -98,7 +99,6 @@ bool job_is_anchor(Job *j);
 
 int job_merge(Job *j, Job *other);
 
 
 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_is_mergeable(JobType a, JobType b);
 bool job_type_is_superset(JobType a, JobType b);
 int job_type_merge(JobType *a, JobType b);
 bool job_type_is_mergeable(JobType a, JobType b);
 bool job_type_is_superset(JobType a, JobType b);
@@ -108,4 +108,10 @@ void job_schedule_run(Job *j);
 int job_run_and_invalidate(Job *j);
 int job_finish_and_invalidate(Job *j, bool success);
 
 int job_run_and_invalidate(Job *j);
 int job_finish_and_invalidate(Job *j, bool success);
 
+const char* job_type_to_string(JobType t);
+JobType job_type_from_string(const char *s);
+
+const char* job_state_to_string(JobState t);
+JobState job_state_from_string(const char *s);
+
 #endif
 #endif
index ac96d0f..4bb1ef0 100644 (file)
@@ -6,6 +6,8 @@
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <sched.h>
+#include <sys/prctl.h>
 
 #include "unit.h"
 #include "strv.h"
 
 #include "unit.h"
 #include "strv.h"
@@ -13,6 +15,8 @@
 #include "load-fragment.h"
 #include "log.h"
 #include "ioprio.h"
 #include "load-fragment.h"
 #include "log.h"
 #include "ioprio.h"
+#include "securebits.h"
+#include "missing.h"
 
 static int config_parse_deps(
                 const char *filename,
 
 static int config_parse_deps(
                 const char *filename,
@@ -405,21 +409,20 @@ static int config_parse_service_type(
                 void *userdata) {
 
         Service *s = data;
                 void *userdata) {
 
         Service *s = data;
+        ServiceType x;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if (streq(rvalue, "forking"))
-                s->type = SERVICE_FORKING;
-        else if (streq(rvalue, "simple"))
-                s->type = SERVICE_SIMPLE;
-        else {
+        if ((x = service_type_from_string(rvalue)) < 0) {
                 log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
                 return -EBADMSG;
         }
 
                 log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
                 return -EBADMSG;
         }
 
+        s->type = x;
+
         return 0;
 }
 
         return 0;
 }
 
@@ -433,23 +436,20 @@ static int config_parse_service_restart(
                 void *userdata) {
 
         Service *s = data;
                 void *userdata) {
 
         Service *s = data;
+        ServiceRestart x;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if (streq(rvalue, "once"))
-                s->restart = SERVICE_ONCE;
-        else if (streq(rvalue, "on-success"))
-                s->type = SERVICE_RESTART_ON_SUCCESS;
-        else if (streq(rvalue, "always"))
-                s->type = SERVICE_RESTART_ALWAYS;
-        else {
-                log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
+        if ((x = service_restart_from_string(rvalue)) < 0) {
+                log_error("[%s:%u] Failed to parse service restart specifier: %s", filename, line, rvalue);
                 return -EBADMSG;
         }
 
                 return -EBADMSG;
         }
 
+        s->restart = x;
+
         return 0;
 }
 
         return 0;
 }
 
@@ -491,26 +491,46 @@ int config_parse_output(
                 void *data,
                 void *userdata) {
 
                 void *data,
                 void *userdata) {
 
-        ExecOutput *o = data;
+        ExecOutput *o = data, x;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if (streq(rvalue, "syslog"))
-                *o = EXEC_SYSLOG;
-        else if (streq(rvalue, "null"))
-                *o = EXEC_NULL;
-        else if (streq(rvalue, "syslog"))
-                *o = EXEC_SYSLOG;
-        else if (streq(rvalue, "kernel"))
-                *o = EXEC_KERNEL;
-        else {
-                log_error("[%s:%u] Failed to parse log output: %s", filename, line, rvalue);
+        if ((x = exec_output_from_string(rvalue)) < 0) {
+                log_error("[%s:%u] Failed to parse output specifier: %s", filename, line, rvalue);
+                return -EBADMSG;
+        }
+
+        *o = x;
+
+        return 0;
+}
+
+int config_parse_input(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecInput *i = data, x;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((x = exec_input_from_string(rvalue)) < 0) {
+                log_error("[%s:%u] Failed to parse input specifier: %s", filename, line, rvalue);
                 return -EBADMSG;
         }
 
                 return -EBADMSG;
         }
 
+        *i = x;
+
         return 0;
 }
 
         return 0;
 }
 
@@ -523,55 +543,23 @@ int config_parse_facility(
                 void *data,
                 void *userdata) {
 
                 void *data,
                 void *userdata) {
 
-        static const char * const table[LOG_NFACILITIES] = {
-                [LOG_FAC(LOG_KERN)] = "kern",
-                [LOG_FAC(LOG_USER)] = "user",
-                [LOG_FAC(LOG_MAIL)] = "mail",
-                [LOG_FAC(LOG_DAEMON)] = "daemon",
-                [LOG_FAC(LOG_AUTH)] = "auth",
-                [LOG_FAC(LOG_SYSLOG)] = "syslog",
-                [LOG_FAC(LOG_LPR)] = "lpr",
-                [LOG_FAC(LOG_NEWS)] = "news",
-                [LOG_FAC(LOG_UUCP)] = "uucp",
-                [LOG_FAC(LOG_CRON)] = "cron",
-                [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
-                [LOG_FAC(LOG_FTP)] = "ftp",
-                [LOG_FAC(LOG_LOCAL0)] = "local0",
-                [LOG_FAC(LOG_LOCAL1)] = "local1",
-                [LOG_FAC(LOG_LOCAL2)] = "local2",
-                [LOG_FAC(LOG_LOCAL3)] = "local3",
-                [LOG_FAC(LOG_LOCAL4)] = "local4",
-                [LOG_FAC(LOG_LOCAL5)] = "local5",
-                [LOG_FAC(LOG_LOCAL6)] = "local6",
-                [LOG_FAC(LOG_LOCAL7)] = "local7"
-        };
 
 
-        ExecOutput *o = data;
-        int i;
+        int *o = data, x;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        for (i = 0; i < (int) ELEMENTSOF(table); i++)
-                if (streq(rvalue, table[i])) {
-                        *o = LOG_MAKEPRI(i, LOG_PRI(*o));
-                        break;
-                }
-
-        if (i >= (int) ELEMENTSOF(table)) {
+        if ((x = log_facility_from_string(rvalue)) < 0)
 
                 /* Second try, let's see if this is a number. */
 
                 /* Second try, let's see if this is a number. */
-                if (safe_atoi(rvalue, &i) >= 0 &&
-                    i >= 0 &&
-                    i < (int) ELEMENTSOF(table))
-                        *o = LOG_MAKEPRI(i, LOG_PRI(*o));
-                else {
-                        log_error("[%s:%u] Failed to parse log output: %s", filename, line, rvalue);
+                if (safe_atoi(rvalue, &x) < 0 || !log_facility_to_string(x)) {
+                        log_error("[%s:%u] Failed to parse log facility: %s", filename, line, rvalue);
                         return -EBADMSG;
                 }
                         return -EBADMSG;
                 }
-        }
+
+        *o = LOG_MAKEPRI(x, LOG_PRI(*o));
 
         return 0;
 }
 
         return 0;
 }
@@ -585,48 +573,86 @@ int config_parse_level(
                 void *data,
                 void *userdata) {
 
                 void *data,
                 void *userdata) {
 
-        static const char * const table[LOG_DEBUG+1] = {
-                [LOG_EMERG] = "emerg",
-                [LOG_ALERT] = "alert",
-                [LOG_CRIT] = "crit",
-                [LOG_ERR] = "err",
-                [LOG_WARNING] = "warning",
-                [LOG_NOTICE] = "notice",
-                [LOG_INFO] = "info",
-                [LOG_DEBUG] = "debug"
-        };
 
 
-        ExecOutput *o = data;
-        int i;
+        int *o = data, x;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        for (i = 0; i < (int) ELEMENTSOF(table); i++)
-                if (streq(rvalue, table[i])) {
-                        *o = LOG_MAKEPRI(LOG_FAC(*o), i);
-                        break;
+        if ((x = log_level_from_string(rvalue)) < 0)
+
+                /* Second try, let's see if this is a number. */
+                if (safe_atoi(rvalue, &x) < 0 || !log_level_to_string(x)) {
+                        log_error("[%s:%u] Failed to parse log level: %s", filename, line, rvalue);
+                        return -EBADMSG;
                 }
 
                 }
 
-        if (i >= LOG_NFACILITIES) {
+        *o = LOG_MAKEPRI(LOG_FAC(*o), x);
+        return 0;
+}
+
+int config_parse_io_class(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecContext *c = data;
+        int x;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((x = ioprio_class_from_string(rvalue)) < 0)
 
                 /* Second try, let's see if this is a number. */
 
                 /* Second try, let's see if this is a number. */
-                if (safe_atoi(rvalue, &i) >= 0 &&
-                    i >= 0 &&
-                    i < (int) ELEMENTSOF(table))
-                        *o = LOG_MAKEPRI(LOG_FAC(*o), i);
-                else {
-                        log_error("[%s:%u] Failed to parse log output: %s", filename, line, rvalue);
+                if (safe_atoi(rvalue, &x) < 0 || !ioprio_class_to_string(x)) {
+                        log_error("[%s:%u] Failed to parse IO scheduling class: %s", filename, line, rvalue);
                         return -EBADMSG;
                 }
                         return -EBADMSG;
                 }
+
+        c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
+        c->ioprio_set = true;
+
+        return 0;
+}
+
+int config_parse_io_priority(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecContext *c = data;
+        int i;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) {
+                log_error("[%s:%u] Failed to parse io priority: %s", filename, line, rvalue);
+                return -EBADMSG;
         }
 
         }
 
+        c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
+        c->ioprio_set = true;
+
         return 0;
 }
 
         return 0;
 }
 
-int config_parse_io_class(
+int config_parse_cpu_sched_policy(
                 const char *filename,
                 unsigned line,
                 const char *section,
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -635,12 +661,37 @@ int config_parse_io_class(
                 void *data,
                 void *userdata) {
 
                 void *data,
                 void *userdata) {
 
-        static const char * const table[] = {
-                [IOPRIO_CLASS_NONE] = NULL,
-                [IOPRIO_CLASS_RT] = "realtime",
-                [IOPRIO_CLASS_BE] = "best-effort",
-                [IOPRIO_CLASS_IDLE] = "idle",
-        };
+
+        ExecContext *c = data;
+        int x;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((x = sched_policy_from_string(rvalue)) < 0)
+
+                /* Second try, let's see if this is a number. */
+                if (safe_atoi(rvalue, &x) < 0 || !sched_policy_to_string(x)) {
+                        log_error("[%s:%u] Failed to parse CPU scheduling policy: %s", filename, line, rvalue);
+                        return -EBADMSG;
+                }
+
+        c->cpu_sched_policy = x;
+        c->cpu_sched_set = true;
+
+        return 0;
+}
+
+int config_parse_cpu_sched_prio(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         ExecContext *c = data;
         int i;
 
         ExecContext *c = data;
         int i;
@@ -650,36 +701,176 @@ int config_parse_io_class(
         assert(rvalue);
         assert(data);
 
         assert(rvalue);
         assert(data);
 
-        for (i = 0; i < (int) ELEMENTSOF(table); i++) {
-                if (!table[i])
-                        continue;
+        /* On Linux RR/FIFO have the same range */
+        if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) {
+                log_error("[%s:%u] Failed to parse CPU scheduling priority: %s", filename, line, rvalue);
+                return -EBADMSG;
+        }
 
 
-                if (streq(rvalue, table[i])) {
-                        c->ioprio = IOPRIO_PRIO_VALUE(i, IOPRIO_PRIO_DATA(c->ioprio));
-                        break;
+        c->cpu_sched_priority = i;
+        c->cpu_sched_set = true;
+
+        return 0;
+}
+
+int config_parse_cpu_affinity(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecContext *c = data;
+        char *w;
+        size_t l;
+        char *state;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        FOREACH_WORD(w, &l, rvalue, state) {
+                char *t;
+                int r;
+                unsigned cpu;
+
+                if (!(t = strndup(w, l)))
+                        return -ENOMEM;
+
+                r = safe_atou(t, &cpu);
+                free(t);
+
+                if (r < 0 || cpu >= CPU_SETSIZE) {
+                        log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue);
+                        return -EBADMSG;
                 }
                 }
+
+                CPU_SET(cpu, &c->cpu_affinity);
         }
 
         }
 
-        if (i >= (int) ELEMENTSOF(table)) {
+        c->cpu_affinity_set = true;
 
 
-                /* Second try, let's see if this is a number. */
-                if (safe_atoi(rvalue, &i) >= 0 &&
-                    i >= 0 &&
-                    i < (int) ELEMENTSOF(table) &&
-                    table[i])
-                        c->ioprio = IOPRIO_PRIO_VALUE(i, IOPRIO_PRIO_DATA(c->ioprio));
+        return 0;
+}
+
+int config_parse_capabilities(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecContext *c = data;
+        cap_t cap;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (!(cap = cap_from_text(rvalue))) {
+                if (errno == ENOMEM)
+                        return -ENOMEM;
+
+                log_error("[%s:%u] Failed to parse capabilities: %s", filename, line, rvalue);
+                return -EBADMSG;
+        }
+
+        if (c->capabilities)
+                cap_free(c->capabilities);
+        c->capabilities = cap;
+
+        return 0;
+}
+
+int config_parse_secure_bits(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecContext *c = data;
+        char *w;
+        size_t l;
+        char *state;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        FOREACH_WORD(w, &l, rvalue, state) {
+                if (first_word(w, "keep-caps"))
+                        c->secure_bits |= SECURE_KEEP_CAPS;
+                else if (first_word(w, "keep-caps-locked"))
+                        c->secure_bits |= SECURE_KEEP_CAPS_LOCKED;
+                else if (first_word(w, "no-setuid-fixup"))
+                        c->secure_bits |= SECURE_NO_SETUID_FIXUP;
+                else if (first_word(w, "no-setuid-fixup-locked"))
+                        c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED;
+                else if (first_word(w, "noroot"))
+                        c->secure_bits |= SECURE_NOROOT;
+                else if (first_word(w, "noroot-locked"))
+                        c->secure_bits |= SECURE_NOROOT_LOCKED;
                 else {
                 else {
-                        log_error("[%s:%u] Failed to parse io priority: %s", filename, line, rvalue);
+                        log_error("[%s:%u] Failed to parse secure bits: %s", filename, line, rvalue);
                         return -EBADMSG;
                 }
         }
 
                         return -EBADMSG;
                 }
         }
 
-        c->ioprio_set = true;
+        return 0;
+}
+
+int config_parse_bounding_set(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecContext *c = data;
+        char *w;
+        size_t l;
+        char *state;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        FOREACH_WORD(w, &l, rvalue, state) {
+                char *t;
+                int r;
+                cap_value_t cap;
+
+                if (!(t = strndup(w, l)))
+                        return -ENOMEM;
+
+                r = cap_from_name(t, &cap);
+                free(t);
+
+                if (r < 0) {
+                        log_error("[%s:%u] Failed to parse capability bounding set: %s", filename, line, rvalue);
+                        return -EBADMSG;
+                }
+
+                c->capability_bounding_set_drop |= 1 << cap;
+        }
 
         return 0;
 }
 
 
         return 0;
 }
 
-int config_parse_io_priority(
+static int config_parse_timer_slack_ns(
                 const char *filename,
                 unsigned line,
                 const char *section,
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -689,24 +880,52 @@ int config_parse_io_priority(
                 void *userdata) {
 
         ExecContext *c = data;
                 void *userdata) {
 
         ExecContext *c = data;
-        int i;
+        unsigned long u;
+        int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if (safe_atoi(rvalue, &i) >= 0 &&
-            i >= 0 &&
-            i < IOPRIO_BE_NR)
-                c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
-        else {
-                log_error("[%s:%u] Failed to parse io priority: %s", filename, line, rvalue);
-                return -EBADMSG;
+        if ((r = safe_atolu(rvalue, &u)) < 0) {
+                log_error("[%s:%u] Failed to parse time slack value: %s", filename, line, rvalue);
+                return r;
         }
 
         }
 
-        c->ioprio_set = true;
+        c->timer_slack_ns = u;
+
+        return 0;
+}
+
+static int config_parse_limit(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        struct rlimit **rl = data;
+        unsigned long long u;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((r = safe_atollu(rvalue, &u)) < 0) {
+                log_error("[%s:%u] Failed to parse resource value: %s", filename, line, rvalue);
+                return r;
+        }
+
+        if (!*rl)
+                if (!(*rl = new(struct rlimit, 1)))
+                        return -ENOMEM;
 
 
+        (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
         return 0;
 }
 
         return 0;
 }
 
@@ -801,14 +1020,38 @@ static int load_from_path(Unit *u, const char *path) {
                 { "SupplementaryGroups",    config_parse_strv,            &(context).supplementary_groups,                 section   }, \
                 { "Nice",                   config_parse_nice,            &(context),                                      section   }, \
                 { "OOMAdjust",              config_parse_oom_adjust,      &(context),                                      section   }, \
                 { "SupplementaryGroups",    config_parse_strv,            &(context).supplementary_groups,                 section   }, \
                 { "Nice",                   config_parse_nice,            &(context),                                      section   }, \
                 { "OOMAdjust",              config_parse_oom_adjust,      &(context),                                      section   }, \
-                { "IOPriority",             config_parse_io_priority,     &(context),                                      section   }, \
                 { "IOSchedulingClass",      config_parse_io_class,        &(context),                                      section   }, \
                 { "IOSchedulingClass",      config_parse_io_class,        &(context),                                      section   }, \
+                { "IOSchedulingPriority",   config_parse_io_priority,     &(context),                                      section   }, \
+                { "CPUSchedulingPolicy",    config_parse_cpu_sched_policy,&(context),                                      section   }, \
+                { "CPUSchedulingPriority",  config_parse_cpu_sched_prio,  &(context),                                      section   }, \
+                { "CPUAffinity",            config_parse_cpu_affinity,    &(context),                                      section   }, \
                 { "UMask",                  config_parse_umask,           &(context).umask,                                section   }, \
                 { "Environment",            config_parse_strv,            &(context).environment,                          section   }, \
                 { "Output",                 config_parse_output,          &(context).output,                               section   }, \
                 { "UMask",                  config_parse_umask,           &(context).umask,                                section   }, \
                 { "Environment",            config_parse_strv,            &(context).environment,                          section   }, \
                 { "Output",                 config_parse_output,          &(context).output,                               section   }, \
+                { "Input",                  config_parse_input,           &(context).input,                                section   }, \
                 { "SyslogIdentifier",       config_parse_string,          &(context).syslog_identifier,                    section   }, \
                 { "SyslogFacility",         config_parse_facility,        &(context).syslog_priority,                      section   }, \
                 { "SyslogIdentifier",       config_parse_string,          &(context).syslog_identifier,                    section   }, \
                 { "SyslogFacility",         config_parse_facility,        &(context).syslog_priority,                      section   }, \
-                { "SyslogLevel",            config_parse_level,           &(context).syslog_priority,                      section   }
+                { "SyslogLevel",            config_parse_level,           &(context).syslog_priority,                      section   }, \
+                { "Capabilities",           config_parse_capabilities,    &(context),                                      section   }, \
+                { "SecureBits",             config_parse_secure_bits,     &(context),                                      section   }, \
+                { "CapabilityBoundingSetDrop", config_parse_bounding_set, &(context),                                      section   }, \
+                { "TimerSlackNS",           config_parse_timer_slack_ns,  &(context),                                      section   }, \
+                { "LimitCPU",               config_parse_limit,           &(context).rlimit[RLIMIT_CPU],                   section   }, \
+                { "LimitFSIZE",             config_parse_limit,           &(context).rlimit[RLIMIT_FSIZE],                 section   }, \
+                { "LimitDATA",              config_parse_limit,           &(context).rlimit[RLIMIT_DATA],                  section   }, \
+                { "LimitSTACK",             config_parse_limit,           &(context).rlimit[RLIMIT_STACK],                 section   }, \
+                { "LimitCORE",              config_parse_limit,           &(context).rlimit[RLIMIT_CORE],                  section   }, \
+                { "LimitRSS",               config_parse_limit,           &(context).rlimit[RLIMIT_RSS],                   section   }, \
+                { "LimitNOFILE",            config_parse_limit,           &(context).rlimit[RLIMIT_NOFILE],                section   }, \
+                { "LimitAS",                config_parse_limit,           &(context).rlimit[RLIMIT_AS],                    section   }, \
+                { "LimitNPROC",             config_parse_limit,           &(context).rlimit[RLIMIT_NPROC],                 section   }, \
+                { "LimitMEMLOCK",           config_parse_limit,           &(context).rlimit[RLIMIT_MEMLOCK],               section   }, \
+                { "LimitLOCKS",             config_parse_limit,           &(context).rlimit[RLIMIT_LOCKS],                 section   }, \
+                { "LimitSIGPENDING",        config_parse_limit,           &(context).rlimit[RLIMIT_SIGPENDING],            section   }, \
+                { "LimitMSGQUEUE",          config_parse_limit,           &(context).rlimit[RLIMIT_MSGQUEUE],              section   }, \
+                { "LimitNICE",              config_parse_limit,           &(context).rlimit[RLIMIT_NICE],                  section   }, \
+                { "LimitRTPRIO",            config_parse_limit,           &(context).rlimit[RLIMIT_RTPRIO],                section   }, \
+                { "LimitRTTIME",            config_parse_limit,           &(context).rlimit[RLIMIT_RTTIME],                section   }
 
         const ConfigItem items[] = {
                 { "Names",                  config_parse_names,           u,                                               "Meta"    },
 
         const ConfigItem items[] = {
                 { "Names",                  config_parse_names,           u,                                               "Meta"    },
@@ -945,7 +1188,7 @@ int unit_load_fragment(Unit *u) {
                 c = NULL;
 
         if (r >= 0 && c &&
                 c = NULL;
 
         if (r >= 0 && c &&
-            (c->output == EXEC_KERNEL || c->output == EXEC_SYSLOG)) {
+            (c->output == EXEC_OUTPUT_KERNEL || c->output == EXEC_OUTPUT_SYSLOG)) {
                 int k;
 
                 /* If syslog or kernel logging is requested, make sure
                 int k;
 
                 /* If syslog or kernel logging is requested, make sure
index e0ffe93..fca1963 100644 (file)
--- a/manager.c
+++ b/manager.c
@@ -1060,7 +1060,7 @@ static int manager_dispatch_sigchld(Manager *m) {
                 if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED)
                         continue;
 
                 if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED)
                         continue;
 
-                log_debug("child %llu died (code=%s, status=%i)", (long long unsigned) si.si_pid, sigchld_code(si.si_code), si.si_status);
+                log_debug("child %llu died (code=%s, status=%i)", (long long unsigned) si.si_pid, sigchld_code_to_string(si.si_code), si.si_status);
 
                 if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
                         continue;
 
                 if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
                         continue;
index 2943da6..b564c98 100644 (file)
--- a/service.c
+++ b/service.c
@@ -26,23 +26,6 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
         [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
 };
 
         [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
 };
 
-static const char* const state_string_table[_SERVICE_STATE_MAX] = {
-        [SERVICE_DEAD] = "dead",
-        [SERVICE_START_PRE] = "start-pre",
-        [SERVICE_START] = "start",
-        [SERVICE_START_POST] = "start-post",
-        [SERVICE_RUNNING] = "running",
-        [SERVICE_RELOAD] = "reload",
-        [SERVICE_STOP] = "stop",
-        [SERVICE_STOP_SIGTERM] = "stop-sigterm",
-        [SERVICE_STOP_SIGKILL] = "stop-sigkill",
-        [SERVICE_STOP_POST] = "stop-post",
-        [SERVICE_FINAL_SIGTERM] = "final-sigterm",
-        [SERVICE_FINAL_SIGKILL] = "final-sigkill",
-        [SERVICE_MAINTAINANCE] = "maintainance",
-        [SERVICE_AUTO_RESTART] = "auto-restart",
-};
-
 static void service_done(Unit *u) {
         Service *s = SERVICE(u);
 
 static void service_done(Unit *u) {
         Service *s = SERVICE(u);
 
@@ -126,15 +109,6 @@ static int service_init(Unit *u) {
 
 static void service_dump(Unit *u, FILE *f, const char *prefix) {
 
 
 static void service_dump(Unit *u, FILE *f, const char *prefix) {
 
-        static const char* const command_table[_SERVICE_EXEC_MAX] = {
-                [SERVICE_EXEC_START_PRE] = "ExecStartPre",
-                [SERVICE_EXEC_START] = "ExecStart",
-                [SERVICE_EXEC_START_POST] = "ExecStartPost",
-                [SERVICE_EXEC_RELOAD] = "ExecReload",
-                [SERVICE_EXEC_STOP] = "ExecStop",
-                [SERVICE_EXEC_STOP_POST] = "ExecStopPost",
-        };
-
         ServiceExecCommand c;
         Service *s = SERVICE(u);
         char *prefix2;
         ServiceExecCommand c;
         Service *s = SERVICE(u);
         char *prefix2;
@@ -147,7 +121,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
 
         fprintf(f,
                 "%sService State: %s\n",
 
         fprintf(f,
                 "%sService State: %s\n",
-                prefix, state_string_table[s->state]);
+                prefix, service_state_to_string(s->state));
 
         if (s->pid_file)
                 fprintf(f,
 
         if (s->pid_file)
                 fprintf(f,
@@ -163,7 +137,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                         continue;
 
                 fprintf(f, "%sā†’ %s:\n",
                         continue;
 
                 fprintf(f, "%sā†’ %s:\n",
-                        prefix, command_table[c]);
+                        prefix, service_exec_command_to_string(c));
 
                 exec_command_dump_list(s->exec_command[c], f, prefix2);
         }
 
                 exec_command_dump_list(s->exec_command[c], f, prefix2);
         }
@@ -333,7 +307,7 @@ static void service_set_state(Service *s, ServiceState state) {
             state == SERVICE_AUTO_RESTART)
                 service_notify_sockets(s);
 
             state == SERVICE_AUTO_RESTART)
                 service_notify_sockets(s);
 
-        log_debug("%s changed %s ā†’ %s", unit_id(UNIT(s)), state_string_table[old_state], state_string_table[state]);
+        log_debug("%s changed %s ā†’ %s", unit_id(UNIT(s)), service_state_to_string(old_state), service_state_to_string(state));
 
         unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
 }
 
         unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
 }
@@ -837,7 +811,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                         s->exec_command[SERVICE_EXEC_START]->exec_status = s->main_exec_status;
                 }
 
                         s->exec_command[SERVICE_EXEC_START]->exec_status = s->main_exec_status;
                 }
 
-                log_debug("%s: main process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status);
+                log_debug("%s: main process exited, code=%s status=%i", unit_id(u), sigchld_code_to_string(code), status);
 
                 /* The service exited, so the service is officially
                  * gone. */
 
                 /* The service exited, so the service is officially
                  * gone. */
@@ -874,7 +848,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                 exec_status_fill(&s->control_command->exec_status, pid, code, status);
                 s->control_pid = 0;
 
                 exec_status_fill(&s->control_command->exec_status, pid, code, status);
                 s->control_pid = 0;
 
-                log_debug("%s: control process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status);
+                log_debug("%s: control process exited, code=%s status=%i", unit_id(u), sigchld_code_to_string(code), status);
 
                 /* If we are shutting things down anyway we
                  * don't care about failing commands. */
 
                 /* If we are shutting things down anyway we
                  * don't care about failing commands. */
@@ -891,7 +865,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                         /* No further commands for this step, so let's
                          * figure out what to do next */
 
                         /* No further commands for this step, so let's
                          * figure out what to do next */
 
-                        log_debug("%s got final SIGCHLD for state %s", unit_id(u), state_string_table[s->state]);
+                        log_debug("%s got final SIGCHLD for state %s", unit_id(u), service_state_to_string(s->state));
 
                         switch (s->state) {
 
 
                         switch (s->state) {
 
@@ -1042,6 +1016,52 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
         }
 }
 
         }
 }
 
+static const char* const service_state_table[_SERVICE_STATE_MAX] = {
+        [SERVICE_DEAD] = "dead",
+        [SERVICE_START_PRE] = "start-pre",
+        [SERVICE_START] = "start",
+        [SERVICE_START_POST] = "start-post",
+        [SERVICE_RUNNING] = "running",
+        [SERVICE_RELOAD] = "reload",
+        [SERVICE_STOP] = "stop",
+        [SERVICE_STOP_SIGTERM] = "stop-sigterm",
+        [SERVICE_STOP_SIGKILL] = "stop-sigkill",
+        [SERVICE_STOP_POST] = "stop-post",
+        [SERVICE_FINAL_SIGTERM] = "final-sigterm",
+        [SERVICE_FINAL_SIGKILL] = "final-sigkill",
+        [SERVICE_MAINTAINANCE] = "maintainance",
+        [SERVICE_AUTO_RESTART] = "auto-restart",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
+
+static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
+        [SERVICE_ONCE] = "once",
+        [SERVICE_RESTART_ON_SUCCESS] = "restart-on-success",
+        [SERVICE_RESTART_ALWAYS] = "restart-on-failure",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
+
+static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
+        [SERVICE_FORKING] = "forking",
+        [SERVICE_SIMPLE] = "simple",
+        [SERVICE_FINISH] = "finish"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
+
+static const char* const service_exec_command_table[_SERVICE_EXEC_MAX] = {
+        [SERVICE_EXEC_START_PRE] = "ExecStartPre",
+        [SERVICE_EXEC_START] = "ExecStart",
+        [SERVICE_EXEC_START_POST] = "ExecStartPost",
+        [SERVICE_EXEC_RELOAD] = "ExecReload",
+        [SERVICE_EXEC_STOP] = "ExecStop",
+        [SERVICE_EXEC_STOP_POST] = "ExecStopPost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand);
+
 const UnitVTable service_vtable = {
         .suffix = ".service",
 
 const UnitVTable service_vtable = {
         .suffix = ".service",
 
index 38b5438..e22fed5 100644 (file)
--- a/service.h
+++ b/service.h
@@ -24,17 +24,23 @@ typedef enum ServiceState {
         SERVICE_MAINTAINANCE,
         SERVICE_AUTO_RESTART,
         _SERVICE_STATE_MAX,
         SERVICE_MAINTAINANCE,
         SERVICE_AUTO_RESTART,
         _SERVICE_STATE_MAX,
+        _SERVICE_STATE_INVALID = -1
 } ServiceState;
 
 typedef enum ServiceRestart {
         SERVICE_ONCE,
         SERVICE_RESTART_ON_SUCCESS,
 } ServiceState;
 
 typedef enum ServiceRestart {
         SERVICE_ONCE,
         SERVICE_RESTART_ON_SUCCESS,
-        SERVICE_RESTART_ALWAYS
+        SERVICE_RESTART_ALWAYS,
+        _SERVICE_RESTART_MAX,
+        _SERVICE_RESTART_INVALID = -1
 } ServiceRestart;
 
 typedef enum ServiceType {
 } ServiceRestart;
 
 typedef enum ServiceType {
-        SERVICE_FORKING,
-        SERVICE_SIMPLE
+        SERVICE_FORKING,  /* forks by itself (i.e. traditional daemons) */
+        SERVICE_SIMPLE,   /* we fork and go on right-away (i.e. modern socket activated daemons)*/
+        SERVICE_FINISH,   /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */
+        _SERVICE_TYPE_MAX,
+        _SERVICE_TYPE_INVALID = -1
 } ServiceType;
 
 typedef enum ServiceExecCommand {
 } ServiceType;
 
 typedef enum ServiceExecCommand {
@@ -44,7 +50,8 @@ typedef enum ServiceExecCommand {
         SERVICE_EXEC_RELOAD,
         SERVICE_EXEC_STOP,
         SERVICE_EXEC_STOP_POST,
         SERVICE_EXEC_RELOAD,
         SERVICE_EXEC_STOP,
         SERVICE_EXEC_STOP_POST,
-        _SERVICE_EXEC_MAX
+        _SERVICE_EXEC_MAX,
+        _SERVICE_EXEC_INVALID = -1
 } ServiceExecCommand;
 
 struct Service {
 } ServiceExecCommand;
 
 struct Service {
@@ -78,4 +85,16 @@ struct Service {
 
 const UnitVTable service_vtable;
 
 
 const UnitVTable service_vtable;
 
+const char* service_state_to_string(ServiceState i);
+ServiceState service_state_from_string(const char *s);
+
+const char* service_restart_to_string(ServiceRestart i);
+ServiceRestart service_restart_from_string(const char *s);
+
+const char* service_type_to_string(ServiceType i);
+ServiceType service_type_from_string(const char *s);
+
+const char* service_exec_command_to_string(ServiceExecCommand i);
+ServiceExecCommand service_exec_command_from_string(const char *s);
+
 #endif
 #endif
index 3bfdf41..dd50765 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -656,7 +656,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         exec_status_fill(&s->control_command->exec_status, pid, code, status);
         s->control_pid = 0;
 
         exec_status_fill(&s->control_command->exec_status, pid, code, status);
         s->control_pid = 0;
 
-        log_debug("%s control process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status);
+        log_debug("%s control process exited, code=%s status=%i", unit_id(u), sigchld_code_to_string(code), status);
 
         if (s->control_command->command_next &&
             (success || (s->state == SOCKET_EXEC_STOP_PRE || s->state == SOCKET_EXEC_STOP_POST))) {
 
         if (s->control_command->command_next &&
             (success || (s->state == SOCKET_EXEC_STOP_PRE || s->state == SOCKET_EXEC_STOP_POST))) {
index 77af676..313e01f 100644 (file)
@@ -4,3 +4,12 @@ Description=systemd Logging Daemon
 [Service]
 ExecStart=/home/lennart/projects/systemd/systemd-logger
 Type=simple
 [Service]
 ExecStart=/home/lennart/projects/systemd/systemd-logger
 Type=simple
+TimerSlackNS=1000000
+OOMAdjust=4
+Capabilities==eip cap_dac_override=ep
+#SecureBits=keep-caps-locked no-setuid-fixup no-setuid-fixup-locked noroot noroot-locked
+LimitCORE=0
+LimitFSIZE=0
+LimitLOCKS=0
+LimitMEMLOCK=0
+LimitNOFILE=512
diff --git a/unit.c b/unit.c
index 839e542..4fbf586 100644 (file)
--- a/unit.c
+++ b/unit.c
@@ -329,33 +329,6 @@ const char *unit_description(Unit *u) {
 
 void unit_dump(Unit *u, FILE *f, const char *prefix) {
 
 
 void unit_dump(Unit *u, FILE *f, const char *prefix) {
 
-        static const char* const load_state_table[_UNIT_LOAD_STATE_MAX] = {
-                [UNIT_STUB] = "stub",
-                [UNIT_LOADED] = "loaded",
-                [UNIT_FAILED] = "failed"
-        };
-
-        static const char* const active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
-                [UNIT_ACTIVE] = "active",
-                [UNIT_INACTIVE] = "inactive",
-                [UNIT_ACTIVATING] = "activating",
-                [UNIT_DEACTIVATING] = "deactivating"
-        };
-
-        static const char* const dependency_table[_UNIT_DEPENDENCY_MAX] = {
-                [UNIT_REQUIRES] = "Requires",
-                [UNIT_SOFT_REQUIRES] = "SoftRequires",
-                [UNIT_WANTS] = "Wants",
-                [UNIT_REQUISITE] = "Requisite",
-                [UNIT_SOFT_REQUISITE] = "SoftRequisite",
-                [UNIT_REQUIRED_BY] = "RequiredBy",
-                [UNIT_SOFT_REQUIRED_BY] = "SoftRequiredBy",
-                [UNIT_WANTED_BY] = "WantedBy",
-                [UNIT_CONFLICTS] = "Conflicts",
-                [UNIT_BEFORE] = "Before",
-                [UNIT_AFTER] = "After",
-        };
-
         char *t;
         UnitDependency d;
         Iterator i;
         char *t;
         UnitDependency d;
         Iterator i;
@@ -374,12 +347,12 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                 "%s\tDescription: %s\n"
                 "%s\tUnit Load State: %s\n"
                 "%s\tUnit Active State: %s\n"
                 "%s\tDescription: %s\n"
                 "%s\tUnit Load State: %s\n"
                 "%s\tUnit Active State: %s\n"
-                "%s\tRecursive Deactivate: %s\n"
+                "%s\tRecursive Stop: %s\n"
                 "%s\tStop When Unneeded: %s\n",
                 prefix, unit_id(u),
                 prefix, unit_description(u),
                 "%s\tStop When Unneeded: %s\n",
                 prefix, unit_id(u),
                 prefix, unit_description(u),
-                prefix, load_state_table[u->meta.load_state],
-                prefix, active_state_table[unit_active_state(u)],
+                prefix, unit_load_state_to_string(u->meta.load_state),
+                prefix, unit_active_state_to_string(unit_active_state(u)),
                 prefix, yes_no(u->meta.recursive_stop),
                 prefix, yes_no(u->meta.stop_when_unneeded));
 
                 prefix, yes_no(u->meta.recursive_stop),
                 prefix, yes_no(u->meta.stop_when_unneeded));
 
@@ -396,7 +369,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                         continue;
 
                 SET_FOREACH(other, u->meta.dependencies[d], i)
                         continue;
 
                 SET_FOREACH(other, u->meta.dependencies[d], i)
-                        fprintf(f, "%s\t%s: %s\n", prefix, dependency_table[d], unit_id(other));
+                        fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), unit_id(other));
         }
 
         if (UNIT_VTABLE(u)->dump)
         }
 
         if (UNIT_VTABLE(u)->dump)
@@ -1030,3 +1003,49 @@ char *unit_name_escape_path(const char *prefix, const char *path, const char *su
 
         return r;
 }
 
         return r;
 }
+
+static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
+        [UNIT_SERVICE] = "service",
+        [UNIT_TIMER] = "timer",
+        [UNIT_SOCKET] = "socket",
+        [UNIT_TARGET] = "target",
+        [UNIT_DEVICE] = "device",
+        [UNIT_MOUNT] = "mount",
+        [UNIT_AUTOMOUNT] = "automount",
+        [UNIT_SNAPSHOT] = "snapshot"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
+
+static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
+        [UNIT_STUB] = "stub",
+        [UNIT_LOADED] = "loaded",
+        [UNIT_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
+
+static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
+        [UNIT_ACTIVE] = "active",
+        [UNIT_INACTIVE] = "inactive",
+        [UNIT_ACTIVATING] = "activating",
+        [UNIT_DEACTIVATING] = "deactivating"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
+
+static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
+        [UNIT_REQUIRES] = "Requires",
+        [UNIT_SOFT_REQUIRES] = "SoftRequires",
+        [UNIT_WANTS] = "Wants",
+        [UNIT_REQUISITE] = "Requisite",
+        [UNIT_SOFT_REQUISITE] = "SoftRequisite",
+        [UNIT_REQUIRED_BY] = "RequiredBy",
+        [UNIT_SOFT_REQUIRED_BY] = "SoftRequiredBy",
+        [UNIT_WANTED_BY] = "WantedBy",
+        [UNIT_CONFLICTS] = "Conflicts",
+        [UNIT_BEFORE] = "Before",
+        [UNIT_AFTER] = "After",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
diff --git a/unit.h b/unit.h
index c12ce0b..6dfc065 100644 (file)
--- a/unit.h
+++ b/unit.h
@@ -41,7 +41,8 @@ enum UnitLoadState {
         UNIT_STUB,
         UNIT_LOADED,
         UNIT_FAILED,
         UNIT_STUB,
         UNIT_LOADED,
         UNIT_FAILED,
-        _UNIT_LOAD_STATE_MAX
+        _UNIT_LOAD_STATE_MAX,
+        _UNIT_LOAD_STATE_INVALID = -1
 };
 
 enum UnitActiveState {
 };
 
 enum UnitActiveState {
@@ -50,7 +51,8 @@ enum UnitActiveState {
         UNIT_INACTIVE,
         UNIT_ACTIVATING,
         UNIT_DEACTIVATING,
         UNIT_INACTIVE,
         UNIT_ACTIVATING,
         UNIT_DEACTIVATING,
-        _UNIT_ACTIVE_STATE_MAX
+        _UNIT_ACTIVE_STATE_MAX,
+        _UNIT_ACTIVE_STATE_INVALID = -1
 };
 
 static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) {
 };
 
 static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) {
@@ -260,4 +262,16 @@ int set_unit_path(const char *p);
 
 char *unit_name_escape_path(const char *prefix, const char *path, const char *suffix);
 
 
 char *unit_name_escape_path(const char *prefix, const char *path, const char *suffix);
 
+const char *unit_type_to_string(UnitType i);
+UnitType unit_type_from_string(const char *s);
+
+const char *unit_load_state_to_string(UnitLoadState i);
+UnitLoadState unit_load_state_from_string(const char *s);
+
+const char *unit_active_state_to_string(UnitActiveState i);
+UnitActiveState unit_active_state_from_string(const char *s);
+
+const char *unit_dependency_to_string(UnitDependency i);
+UnitDependency unit_dependency_from_string(const char *s);
+
 #endif
 #endif