diff options
-rw-r--r-- | execute.c | 218 | ||||
-rw-r--r-- | execute.h | 52 | ||||
-rw-r--r-- | fixme | 2 | ||||
-rw-r--r-- | job.c | 49 | ||||
-rw-r--r-- | job.h | 10 | ||||
-rw-r--r-- | load-fragment.c | 483 | ||||
-rw-r--r-- | manager.c | 2 | ||||
-rw-r--r-- | service.c | 84 | ||||
-rw-r--r-- | service.h | 27 | ||||
-rw-r--r-- | socket.c | 2 | ||||
-rw-r--r-- | test1/systemd-logger.service | 9 | ||||
-rw-r--r-- | unit.c | 81 | ||||
-rw-r--r-- | unit.h | 18 |
13 files changed, 786 insertions, 251 deletions
@@ -9,6 +9,8 @@ #include <signal.h> #include <sys/socket.h> #include <sys/un.h> +#include <sys/prctl.h> +#include <sched.h> #include "execute.h" #include "strv.h" @@ -16,6 +18,7 @@ #include "util.h" #include "log.h" #include "ioprio.h" +#include "securebits.h" 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) { - case EXEC_CONSOLE: + case EXEC_OUTPUT_CONSOLE: 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; - case EXEC_KERNEL: - case EXEC_SYSLOG: { + case EXEC_OUTPUT_KERNEL: + case EXEC_OUTPUT_SYSLOG: { int fd; union { @@ -187,9 +189,6 @@ static int setup_output(const ExecContext *context, const char *ident) { 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); @@ -237,15 +236,37 @@ static int setup_output(const ExecContext *context, const char *ident) { "%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; } + + 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) { @@ -281,6 +302,11 @@ int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, 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; @@ -315,12 +341,36 @@ int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, 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, ¶m) < 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->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) { @@ -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] = { @@ -383,17 +440,24 @@ void exec_context_init(ExecContext *c) { 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->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->secure_bits = 0; + c->capability_bounding_set_drop = 0; } 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; + + if (c->capabilities) { + cap_free(c->capabilities); + c->capabilities = NULL; + } } 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) { - - 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); @@ -464,13 +528,17 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { 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 : "/"); + 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", @@ -481,12 +549,98 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%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", - prefix, table[IOPRIO_PRIO_CLASS(c->ioprio)], + prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(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) { @@ -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); } + +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); @@ -12,6 +12,7 @@ typedef struct ExecContext ExecContext; #include <sys/capability.h> #include <stdbool.h> #include <stdio.h> +#include <sched.h> #include "list.h" #include "util.h" @@ -20,12 +21,21 @@ typedef struct ExecContext ExecContext; #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; +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; @@ -43,27 +53,37 @@ struct ExecCommand { 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; + 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 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; - /* FIXME: all privs related settings need parser and enforcer */ + /* FIXME: all privs related settings need to be enforced */ 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 - * names are resolved on execution only. */ + * names are resolved on execution only and in the child + * process. */ 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. + * c-> * * 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_INPUT, 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); @@ -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); +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 @@ -52,3 +52,5 @@ - 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 @@ -3,8 +3,12 @@ #include <assert.h> #include <errno.h> +#include "set.h" +#include "unit.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) { @@ -109,30 +113,8 @@ void job_dependency_delete(Job *subject, Job *object, bool *matters) { 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) { - static const char* const job_state_table[_JOB_STATE_MAX] = { - [JOB_WAITING] = "waiting", - [JOB_RUNNING] = "running" - }; 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), - prefix, job_state_table[j->state], + prefix, job_state_to_string(j->state), 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; } + +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); @@ -39,7 +39,8 @@ enum JobType { enum JobState { JOB_WAITING, JOB_RUNNING, - _JOB_STATE_MAX + _JOB_STATE_MAX, + _JOB_STATE_INVALID = -1 }; enum JobMode { @@ -98,7 +99,6 @@ bool job_is_anchor(Job *j); 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); @@ -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); +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 diff --git a/load-fragment.c b/load-fragment.c index ac96d0f746..4bb1ef0108 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -6,6 +6,8 @@ #include <string.h> #include <unistd.h> #include <fcntl.h> +#include <sched.h> +#include <sys/prctl.h> #include "unit.h" #include "strv.h" @@ -13,6 +15,8 @@ #include "load-fragment.h" #include "log.h" #include "ioprio.h" +#include "securebits.h" +#include "missing.h" static int config_parse_deps( const char *filename, @@ -405,21 +409,20 @@ static int config_parse_service_type( void *userdata) { Service *s = data; + ServiceType x; 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; } + s->type = x; + return 0; } @@ -433,23 +436,20 @@ static int config_parse_service_restart( void *userdata) { Service *s = data; + ServiceRestart x; 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; } + s->restart = x; + return 0; } @@ -491,26 +491,46 @@ int config_parse_output( void *data, void *userdata) { - ExecOutput *o = data; + ExecOutput *o = data, x; 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; } + *i = x; + return 0; } @@ -523,55 +543,23 @@ int config_parse_facility( 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); - 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. */ - 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; } - } + + *o = LOG_MAKEPRI(x, LOG_PRI(*o)); return 0; } @@ -585,48 +573,86 @@ int config_parse_level( 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); - 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. */ - 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; } + + 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; } -int config_parse_io_class( +int config_parse_cpu_sched_policy( const char *filename, unsigned line, const char *section, @@ -635,12 +661,37 @@ int config_parse_io_class( 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; @@ -650,36 +701,176 @@ int config_parse_io_class( 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 { - 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; } } - 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; } -int config_parse_io_priority( +static int config_parse_timer_slack_ns( const char *filename, unsigned line, const char *section, @@ -689,24 +880,52 @@ int config_parse_io_priority( void *userdata) { ExecContext *c = data; - int i; + unsigned long u; + int r; 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; } @@ -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 }, \ - { "IOPriority", config_parse_io_priority, &(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 }, \ + { "Input", config_parse_input, &(context).input, 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" }, @@ -945,7 +1188,7 @@ int unit_load_fragment(Unit *u) { 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 @@ -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; - 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; @@ -26,23 +26,6 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [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); @@ -126,15 +109,6 @@ static int service_init(Unit *u) { 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; @@ -147,7 +121,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { 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, @@ -163,7 +137,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { 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); } @@ -333,7 +307,7 @@ static void service_set_state(Service *s, ServiceState state) { 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]); } @@ -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; } - 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. */ @@ -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; - 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. */ @@ -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 */ - 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) { @@ -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", @@ -24,17 +24,23 @@ typedef enum ServiceState { SERVICE_MAINTAINANCE, SERVICE_AUTO_RESTART, _SERVICE_STATE_MAX, + _SERVICE_STATE_INVALID = -1 } 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 { - 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 { @@ -44,7 +50,8 @@ typedef enum ServiceExecCommand { SERVICE_EXEC_RELOAD, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST, - _SERVICE_EXEC_MAX + _SERVICE_EXEC_MAX, + _SERVICE_EXEC_INVALID = -1 } ServiceExecCommand; struct Service { @@ -78,4 +85,16 @@ struct Service { 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 @@ -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; - 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))) { diff --git a/test1/systemd-logger.service b/test1/systemd-logger.service index 77af676351..313e01f0f4 100644 --- a/test1/systemd-logger.service +++ b/test1/systemd-logger.service @@ -4,3 +4,12 @@ Description=systemd Logging Daemon [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 @@ -329,33 +329,6 @@ const char *unit_description(Unit *u) { void unit_dump(Unit *u, FILE *f, const char *prefix) { - static const char* const load_state_table[_UNIT_LOAD_STATE_MAX] = { - [UNIT_STUB] = "stub", - [UNIT_LOADED] = "loaded", - [UNIT_FAILED] = "failed" - }; - - static const char* const active_state_table[_UNIT_ACTIVE_STATE_MAX] = { - [UNIT_ACTIVE] = "active", - [UNIT_INACTIVE] = "inactive", - [UNIT_ACTIVATING] = "activating", - [UNIT_DEACTIVATING] = "deactivating" - }; - - static const char* const dependency_table[_UNIT_DEPENDENCY_MAX] = { - [UNIT_REQUIRES] = "Requires", - [UNIT_SOFT_REQUIRES] = "SoftRequires", - [UNIT_WANTS] = "Wants", - [UNIT_REQUISITE] = "Requisite", - [UNIT_SOFT_REQUISITE] = "SoftRequisite", - [UNIT_REQUIRED_BY] = "RequiredBy", - [UNIT_SOFT_REQUIRED_BY] = "SoftRequiredBy", - [UNIT_WANTED_BY] = "WantedBy", - [UNIT_CONFLICTS] = "Conflicts", - [UNIT_BEFORE] = "Before", - [UNIT_AFTER] = "After", - }; - char *t; UnitDependency d; Iterator i; @@ -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\tRecursive Deactivate: %s\n" + "%s\tRecursive Stop: %s\n" "%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)); @@ -396,7 +369,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { 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) @@ -1030,3 +1003,49 @@ char *unit_name_escape_path(const char *prefix, const char *path, const char *su 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); @@ -41,7 +41,8 @@ enum UnitLoadState { UNIT_STUB, UNIT_LOADED, UNIT_FAILED, - _UNIT_LOAD_STATE_MAX + _UNIT_LOAD_STATE_MAX, + _UNIT_LOAD_STATE_INVALID = -1 }; enum UnitActiveState { @@ -50,7 +51,8 @@ enum UnitActiveState { 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) { @@ -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); +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 |