]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add ExecStartXYZEx= with dbus support for executable prefixes 11778/head
authorAnita Zhang <the.anitazha@gmail.com>
Wed, 20 Feb 2019 22:53:58 +0000 (14:53 -0800)
committerAnita Zhang <the.anitazha@gmail.com>
Fri, 31 May 2019 03:41:42 +0000 (20:41 -0700)
Closes #11654

src/core/dbus-execute.c
src/core/dbus-execute.h
src/core/dbus-service.c
src/core/execute.h
src/core/service.c
src/core/service.h
src/shared/bus-unit-util.c
src/shared/exec-util.c
src/shared/exec-util.h
src/systemctl/systemctl.c
src/test/test-exec-util.c

index d7d339b9d792538b762236912f91cf2154c3c685..bc9a090d8d926a158c5e7a6f39cf8b715f68a1b3 100644 (file)
@@ -826,6 +826,50 @@ static int append_exec_command(sd_bus_message *reply, ExecCommand *c) {
         return sd_bus_message_close_container(reply);
 }
 
+static int append_exec_ex_command(sd_bus_message *reply, ExecCommand *c) {
+        _cleanup_strv_free_ char **ex_opts = NULL;
+        int r;
+
+        assert(reply);
+        assert(c);
+
+        if (!c->path)
+                return 0;
+
+        r = sd_bus_message_open_container(reply, 'r', "sasasttttuii");
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "s", c->path);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append_strv(reply, c->argv);
+        if (r < 0)
+                return r;
+
+        r = exec_command_flags_to_strv(c->flags, &ex_opts);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append_strv(reply, ex_opts);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "ttttuii",
+                                  c->exec_status.start_timestamp.realtime,
+                                  c->exec_status.start_timestamp.monotonic,
+                                  c->exec_status.exit_timestamp.realtime,
+                                  c->exec_status.exit_timestamp.monotonic,
+                                  (uint32_t) c->exec_status.pid,
+                                  (int32_t) c->exec_status.code,
+                                  (int32_t) c->exec_status.status);
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_close_container(reply);
+}
+
 int bus_property_get_exec_command(
                 sd_bus *bus,
                 const char *path,
@@ -880,6 +924,47 @@ int bus_property_get_exec_command_list(
         return sd_bus_message_close_container(reply);
 }
 
+int bus_property_get_exec_ex_command_list(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *ret_error) {
+
+        ExecCommand *c, *exec_command = *(ExecCommand**) userdata;
+        int r;
+
+        assert(bus);
+        assert(reply);
+
+        r = sd_bus_message_open_container(reply, 'a', "(sasasttttuii)");
+        if (r < 0)
+                return r;
+
+        LIST_FOREACH(command, c, exec_command) {
+                r = append_exec_ex_command(reply, c);
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_bus_message_close_container(reply);
+}
+
+static char *exec_command_flags_to_exec_chars(ExecCommandFlags flags) {
+        char *res = NULL;
+
+        asprintf(&res, "%s%s%s%s%s",
+                FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE)   ? "-" : "",
+                FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND)    ? ":" : "",
+                FLAGS_SET(flags, EXEC_COMMAND_FULLY_PRIVILEGED) ? "+" : "",
+                FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID)        ? "!" : "",
+                FLAGS_SET(flags, EXEC_COMMAND_AMBIENT_MAGIC)    ? "!!" : "");
+
+        return res;
+}
+
 int bus_set_transient_exec_command(
                 Unit *u,
                 const char *name,
@@ -887,15 +972,16 @@ int bus_set_transient_exec_command(
                 sd_bus_message *message,
                 UnitWriteFlags flags,
                 sd_bus_error *error) {
+        bool is_ex_prop = endswith(name, "Ex");
         unsigned n = 0;
         int r;
 
-        r = sd_bus_message_enter_container(message, 'a', "(sasb)");
+        r = sd_bus_message_enter_container(message, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
         if (r < 0)
                 return r;
 
-        while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) {
-                _cleanup_strv_free_ char **argv = NULL;
+        while ((r = sd_bus_message_enter_container(message, 'r', is_ex_prop ? "sasas" : "sasb")) > 0) {
+                _cleanup_strv_free_ char **argv = NULL, **ex_opts = NULL;
                 const char *path;
                 int b;
 
@@ -910,7 +996,7 @@ int bus_set_transient_exec_command(
                 if (r < 0)
                         return r;
 
-                r = sd_bus_message_read(message, "b", &b);
+                r = is_ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
                 if (r < 0)
                         return r;
 
@@ -933,7 +1019,12 @@ int bus_set_transient_exec_command(
 
                         c->argv = TAKE_PTR(argv);
 
-                        c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
+                        if (is_ex_prop) {
+                                r = exec_command_flags_from_strv(ex_opts, &c->flags);
+                                if (r < 0)
+                                        return r;
+                        } else
+                                c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
 
                         path_simplify(c->path, false);
                         exec_command_append_list(exec_command, c);
@@ -964,7 +1055,7 @@ int bus_set_transient_exec_command(
                 fputs("ExecStart=\n", f);
 
                 LIST_FOREACH(command, c, *exec_command) {
-                        _cleanup_free_ char *a = NULL, *t = NULL;
+                        _cleanup_free_ char *a = NULL, *t = NULL, *exec_chars = NULL;
                         const char *p;
 
                         p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t);
@@ -975,11 +1066,11 @@ int bus_set_transient_exec_command(
                         if (!a)
                                 return -ENOMEM;
 
-                        fprintf(f, "%s=%s@%s %s\n",
-                                name,
-                                c->flags & EXEC_COMMAND_IGNORE_FAILURE ? "-" : "",
-                                p,
-                                a);
+                        exec_chars = exec_command_flags_to_exec_chars(c->flags);
+                        if (!exec_chars)
+                                return -ENOMEM;
+
+                        fprintf(f, "%s=%s@%s %s\n", name, exec_chars, p, a);
                 }
 
                 r = fflush_and_check(f);
index 84051700ab0ac22727ab8d5e1b6cd00eeabfe86d..cbb2f97cf1dae2f46661d9c14b9bf2ab631cf623 100644 (file)
 #define BUS_EXEC_COMMAND_LIST_VTABLE(name, offset, flags)                    \
         SD_BUS_PROPERTY(name, "a(sasbttttuii)", bus_property_get_exec_command_list, offset, flags)
 
+#define BUS_EXEC_EX_COMMAND_LIST_VTABLE(name, offset, flags)                    \
+        SD_BUS_PROPERTY(name, "a(sasasttttuii)", bus_property_get_exec_ex_command_list, offset, flags)
+
 extern const sd_bus_vtable bus_exec_vtable[];
 
 int bus_property_get_exec_output(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
 int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
 int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
+int bus_property_get_exec_ex_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
 
 int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
 int bus_set_transient_exec_command(Unit *u, const char *name, ExecCommand **exec_command, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
index 824955b944d8e8fe21273f2b72e680529acce787..bc7ffb81eb4564cfe4e3db250a30fcdcf003ffc3 100644 (file)
@@ -133,8 +133,11 @@ const sd_bus_vtable bus_service_vtable[] = {
 
         BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+        BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStartPreEx", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+        BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStartEx", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+        BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStartPostEx", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
@@ -387,6 +390,7 @@ static int bus_service_set_transient_property(
                 return bus_set_transient_exit_status(u, name, &s->success_status, message, flags, error);
 
         ci = service_exec_command_from_string(name);
+        ci = (ci >= 0) ? ci : service_exec_ex_command_from_string(name);
         if (ci >= 0)
                 return bus_set_transient_exec_command(u, name, &s->exec_command[ci], message, flags, error);
 
index 7ddc36e6f3d5289789ae384344598e7a4f73f9e0..780876826f2fd0afdb4adf34f7be011412416068 100644 (file)
@@ -15,6 +15,7 @@ typedef struct Manager Manager;
 
 #include "cgroup-util.h"
 #include "cpu-set-util.h"
+#include "exec-util.h"
 #include "fdset.h"
 #include "list.h"
 #include "missing_resource.h"
@@ -88,14 +89,6 @@ struct ExecStatus {
         int status;   /* as in sigingo_t::si_status */
 };
 
-typedef enum ExecCommandFlags {
-        EXEC_COMMAND_IGNORE_FAILURE   = 1 << 0,
-        EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
-        EXEC_COMMAND_NO_SETUID        = 1 << 2,
-        EXEC_COMMAND_AMBIENT_MAGIC    = 1 << 3,
-        EXEC_COMMAND_NO_ENV_EXPAND    = 1 << 4,
-} ExecCommandFlags;
-
 /* Stores information about commands we execute. Covers both configuration settings as well as runtime data. */
 struct ExecCommand {
         char *path;
index f16601a7288acc36353ac766cc85e9aa307a15f8..190d84e56a98cfa8001b3dd7257b617f6079cc83 100644 (file)
@@ -4188,6 +4188,14 @@ static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] =
 
 DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand);
 
+static const char* const service_exec_ex_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
+        [SERVICE_EXEC_START_PRE] = "ExecStartPreEx",
+        [SERVICE_EXEC_START] = "ExecStartEx",
+        [SERVICE_EXEC_START_POST] = "ExecStartPostEx",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_exec_ex_command, ServiceExecCommand);
+
 static const char* const notify_state_table[_NOTIFY_STATE_MAX] = {
         [NOTIFY_UNKNOWN] = "unknown",
         [NOTIFY_READY] = "ready",
index 2aebc5f09d87eacffe6a6a26701ce7b7cb241df9..d6182dbaa0295016aeff0cd169714ccedba7281e 100644 (file)
@@ -210,6 +210,9 @@ ServiceType service_type_from_string(const char *s) _pure_;
 const char* service_exec_command_to_string(ServiceExecCommand i) _const_;
 ServiceExecCommand service_exec_command_from_string(const char *s) _pure_;
 
+const char* service_exec_ex_command_to_string(ServiceExecCommand i) _const_;
+ServiceExecCommand service_exec_ex_command_from_string(const char *s) _pure_;
+
 const char* notify_state_to_string(NotifyState i) _const_;
 NotifyState notify_state_from_string(const char *s) _pure_;
 
index fb86391975373949f986e2ff7091dc4a4fea284c..fd26b86359b5aa470d16b02b681c902358f2ec8b 100644 (file)
@@ -9,6 +9,7 @@
 #include "condition.h"
 #include "cpu-set-util.h"
 #include "escape.h"
+#include "exec-util.h"
 #include "hexdecoct.h"
 #include "hostname-util.h"
 #include "in-addr-util.h"
@@ -244,19 +245,21 @@ static int bus_append_parse_size(sd_bus_message *m, const char *field, const cha
 }
 
 static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) {
-        bool ignore_failure = false, explicit_path = false, done = false;
-        _cleanup_strv_free_ char **l = NULL;
-        _cleanup_free_ char *path = NULL;
+        bool explicit_path = false, done = false;
+        _cleanup_strv_free_ char **l = NULL, **ex_opts = NULL;
+        _cleanup_free_ char *path = NULL, *upgraded_name = NULL;
+        ExecCommandFlags flags = 0;
+        bool is_ex_prop = endswith(field, "Ex");
         int r;
 
         do {
                 switch (*eq) {
 
                 case '-':
-                        if (ignore_failure)
+                        if (FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE))
                                 done = true;
                         else {
-                                ignore_failure = true;
+                                flags |= EXEC_COMMAND_IGNORE_FAILURE;
                                 eq++;
                         }
                         break;
@@ -270,11 +273,36 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
                         }
                         break;
 
+                case ':':
+                        if (FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND))
+                                done = true;
+                        else {
+                                flags |= EXEC_COMMAND_NO_ENV_EXPAND;
+                                eq++;
+                        }
+                        break;
+
                 case '+':
+                        if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))
+                                done = true;
+                        else {
+                                flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
+                                eq++;
+                        }
+                        break;
+
                 case '!':
-                        /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
-                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
-                                               "Sorry, but +, ! and !! are currently not supported for transient services.");
+                        if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))
+                                done = true;
+                        else if (FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID)) {
+                                flags &= ~EXEC_COMMAND_NO_SETUID;
+                                flags |= EXEC_COMMAND_AMBIENT_MAGIC;
+                                eq++;
+                        } else {
+                                flags |= EXEC_COMMAND_NO_SETUID;
+                                eq++;
+                        }
+                        break;
 
                 default:
                         done = true;
@@ -282,6 +310,20 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
                 }
         } while (!done);
 
+        if (!is_ex_prop && (flags & (EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))) {
+                /* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
+                is_ex_prop = true;
+                upgraded_name = strappend(field, "Ex");
+                if (!upgraded_name)
+                        return log_oom();
+        }
+
+        if (is_ex_prop) {
+                r = exec_command_flags_to_strv(flags, &ex_opts);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
+        }
+
         if (explicit_path) {
                 r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
                 if (r < 0)
@@ -296,21 +338,21 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+        r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, upgraded_name ?: field);
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_open_container(m, 'v', "a(sasb)");
+        r = sd_bus_message_open_container(m, 'v', is_ex_prop ? "a(sasas)" : "a(sasb)");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_open_container(m, 'a', "(sasb)");
+        r = sd_bus_message_open_container(m, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
         if (r < 0)
                 return bus_log_create_error(r);
 
         if (!strv_isempty(l)) {
 
-                r = sd_bus_message_open_container(m, 'r', "sasb");
+                r = sd_bus_message_open_container(m, 'r', is_ex_prop ? "sasas" : "sasb");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -322,7 +364,7 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
                 if (r < 0)
                         return bus_log_create_error(r);
 
-                r = sd_bus_message_append(m, "b", ignore_failure);
+                r = is_ex_prop ? sd_bus_message_append_strv(m, ex_opts) : sd_bus_message_append(m, "b", FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1351,8 +1393,8 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
 
         if (STR_IN_SET(field,
                        "ExecStartPre", "ExecStart", "ExecStartPost",
+                       "ExecStartPreEx", "ExecStartEx", "ExecStartPostEx",
                        "ExecReload", "ExecStop", "ExecStopPost"))
-
                 return bus_append_exec_command(m, field, eq);
 
         if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
index add7fe4c141f45d82efc25a700cb0eb5792f9cd8..8fb936dcce45ac3effd6f7cadd13040a15410c11 100644 (file)
@@ -22,6 +22,7 @@
 #include "set.h"
 #include "signal-util.h"
 #include "stat-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 #include "terminal-util.h"
@@ -365,8 +366,81 @@ static int gather_environment_consume(int fd, void *arg) {
         return r;
 }
 
+int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags) {
+        ExecCommandFlags ex_flag, ret_flags = 0;
+        char **opt;
+
+        assert(flags);
+
+        STRV_FOREACH(opt, ex_opts) {
+                ex_flag = exec_command_flags_from_string(*opt);
+                if (ex_flag >= 0)
+                        ret_flags |= ex_flag;
+                else
+                        return -EINVAL;
+        }
+
+        *flags = ret_flags;
+
+        return 0;
+}
+
+int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ex_opts) {
+        _cleanup_strv_free_ char **ret_opts = NULL;
+        ExecCommandFlags it = flags;
+        const char *str;
+        int i, r;
+
+        assert(ex_opts);
+
+        for (i = 0; it != 0; it &= ~(1 << i), i++) {
+                if (FLAGS_SET(flags, (1 << i))) {
+                        str = exec_command_flags_to_string(1 << i);
+                        if (!str)
+                                return -EINVAL;
+
+                        r = strv_extend(&ret_opts, str);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        *ex_opts = TAKE_PTR(ret_opts);
+
+        return 0;
+}
+
 const gather_stdout_callback_t gather_environment[] = {
         gather_environment_generate,
         gather_environment_collect,
         gather_environment_consume,
 };
+
+static const char* const exec_command_strings[] = {
+        "ignore-failure", /* EXEC_COMMAND_IGNORE_FAILURE */
+        "privileged",     /* EXEC_COMMAND_FULLY_PRIVILEGED */
+        "no-setuid",      /* EXEC_COMMAND_NO_SETUID */
+        "ambient",        /* EXEC_COMMAND_AMBIENT_MAGIC */
+        "no-env-expand",  /* EXEC_COMMAND_NO_ENV_EXPAND */
+};
+
+const char* exec_command_flags_to_string(ExecCommandFlags i) {
+        size_t idx;
+
+        for (idx = 0; idx < ELEMENTSOF(exec_command_strings); idx++)
+                if (i == (1 << idx))
+                        return exec_command_strings[idx];
+
+        return NULL;
+}
+
+ExecCommandFlags exec_command_flags_from_string(const char *s) {
+        ssize_t idx;
+
+        idx = string_table_lookup(exec_command_strings, ELEMENTSOF(exec_command_strings), s);
+
+        if (idx < 0)
+                return _EXEC_COMMAND_FLAGS_INVALID;
+        else
+                return 1 << idx;
+}
index 5b75a40229bf9a64593354e6e1372fdad51215b6..9fe9012516cbb752e18123dbc5b7222cf14358f1 100644 (file)
@@ -20,6 +20,15 @@ typedef enum {
         EXEC_DIR_IGNORE_ERRORS = 1 << 1, /* Ignore non-zero exit status of scripts */
 } ExecDirFlags;
 
+typedef enum ExecCommandFlags {
+        EXEC_COMMAND_IGNORE_FAILURE   = 1 << 0,
+        EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
+        EXEC_COMMAND_NO_SETUID        = 1 << 2,
+        EXEC_COMMAND_AMBIENT_MAGIC    = 1 << 3,
+        EXEC_COMMAND_NO_ENV_EXPAND    = 1 << 4,
+        _EXEC_COMMAND_FLAGS_INVALID   = -1,
+} ExecCommandFlags;
+
 int execute_directories(
                 const char* const* directories,
                 usec_t timeout,
@@ -29,4 +38,10 @@ int execute_directories(
                 char *envp[],
                 ExecDirFlags flags);
 
+int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags);
+int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ex_opts);
+
 extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
+
+const char* exec_command_flags_to_string(ExecCommandFlags i);
+ExecCommandFlags exec_command_flags_from_string(const char *s);
index cca150830979a317eca98b14434bae6eb1c8929a..a1ec281162b1cf8d98a024e87535fd8ad4084833 100644 (file)
@@ -36,6 +36,7 @@
 #include "efivars.h"
 #include "env-util.h"
 #include "escape.h"
+#include "exec-util.h"
 #include "exit-status.h"
 #include "fd-util.h"
 #include "format-util.h"
@@ -3955,6 +3956,8 @@ typedef struct ExecStatusInfo {
         int code;
         int status;
 
+        ExecCommandFlags flags;
+
         LIST_FIELDS(struct ExecStatusInfo, exec);
 } ExecStatusInfo;
 
@@ -3967,7 +3970,8 @@ static void exec_status_info_free(ExecStatusInfo *i) {
         free(i);
 }
 
-static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
+static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i, bool is_ex_prop) {
+        _cleanup_strv_free_ char **ex_opts = NULL;
         uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
         const char *path;
         uint32_t pid;
@@ -3977,7 +3981,7 @@ static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
         assert(m);
         assert(i);
 
-        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, "sasbttttuii");
+        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, is_ex_prop ? "sasasttttuii" : "sasbttttuii");
         if (r < 0)
                 return bus_log_parse_error(r);
         else if (r == 0)
@@ -3995,9 +3999,12 @@ static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
+        r = is_ex_prop ? sd_bus_message_read_strv(m, &ex_opts) : sd_bus_message_read(m, "b", &ignore);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
         r = sd_bus_message_read(m,
-                                "bttttuii",
-                                &ignore,
+                                "ttttuii",
                                 &start_timestamp, &start_timestamp_monotonic,
                                 &exit_timestamp, &exit_timestamp_monotonic,
                                 &pid,
@@ -4005,7 +4012,15 @@ static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        i->ignore = ignore;
+        if (is_ex_prop) {
+                r = exec_command_flags_from_strv(ex_opts, &i->flags);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to convert strv to ExecCommandFlags: %m");
+
+                i->ignore = FLAGS_SET(i->flags, EXEC_COMMAND_IGNORE_FAILURE);
+        } else
+                i->ignore = ignore;
+
         i->start_timestamp = (usec_t) start_timestamp;
         i->exit_timestamp = (usec_t) exit_timestamp;
         i->pid = (pid_t) pid;
@@ -4749,9 +4764,10 @@ static int map_exec(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_e
         _cleanup_free_ ExecStatusInfo *info = NULL;
         ExecStatusInfo *last;
         UnitStatusInfo *i = userdata;
+        bool is_ex_prop = endswith(member, "Ex");
         int r;
 
-        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
+        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, is_ex_prop ? "(sasasttttuii)" : "(sasbttttuii)");
         if (r < 0)
                 return r;
 
@@ -4761,7 +4777,7 @@ static int map_exec(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_e
 
         LIST_FIND_TAIL(exec, i->exec, last);
 
-        while ((r = exec_status_info_deserialize(m, info)) > 0) {
+        while ((r = exec_status_info_deserialize(m, info, is_ex_prop)) > 0) {
 
                 info->name = strdup(member);
                 if (!info->name)
@@ -5092,29 +5108,51 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
 
                 } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) {
                         ExecStatusInfo info = {};
+                        bool is_ex_prop = endswith(name, "Ex");
 
-                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
+                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, is_ex_prop ? "(sasasttttuii)" : "(sasbttttuii)");
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
-                        while ((r = exec_status_info_deserialize(m, &info)) > 0) {
+                        while ((r = exec_status_info_deserialize(m, &info, is_ex_prop)) > 0) {
                                 char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
-                                _cleanup_free_ char *tt;
+                                _cleanup_strv_free_ char **optv = NULL;
+                                _cleanup_free_ char *tt, *o = NULL;
 
                                 tt = strv_join(info.argv, " ");
 
-                                 bus_print_property_valuef(name, expected_value, value,
-                                                           "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
-                                                           strna(info.path),
-                                                           strna(tt),
-                                                           yes_no(info.ignore),
-                                                           strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
-                                                           strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
-                                                           info.pid,
-                                                           sigchld_code_to_string(info.code),
-                                                           info.status,
-                                                           info.code == CLD_EXITED ? "" : "/",
-                                                           strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+                                if (is_ex_prop) {
+                                        r = exec_command_flags_to_strv(info.flags, &optv);
+                                        if (r < 0)
+                                                return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
+
+                                        o = strv_join(optv, " ");
+
+                                        bus_print_property_valuef(name, expected_value, value,
+                                                                  "{ path=%s ; argv[]=%s ; flags=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
+                                                                  strna(info.path),
+                                                                  strna(tt),
+                                                                  strna(o),
+                                                                  strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+                                                                  strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+                                                                  info.pid,
+                                                                  sigchld_code_to_string(info.code),
+                                                                  info.status,
+                                                                  info.code == CLD_EXITED ? "" : "/",
+                                                                  strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+                                } else
+                                        bus_print_property_valuef(name, expected_value, value,
+                                                                  "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
+                                                                  strna(info.path),
+                                                                  strna(tt),
+                                                                  yes_no(info.ignore),
+                                                                  strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+                                                                  strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+                                                                  info.pid,
+                                                                  sigchld_code_to_string(info.code),
+                                                                  info.status,
+                                                                  info.code == CLD_EXITED ? "" : "/",
+                                                                  strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
 
                                 free(info.path);
                                 strv_free(info.argv);
@@ -5459,82 +5497,85 @@ static int show_one(
                 bool *ellipsized) {
 
         static const struct bus_properties_map property_map[] = {
-                { "LoadState",                      "s",              NULL,           offsetof(UnitStatusInfo, load_state)                        },
-                { "ActiveState",                    "s",              NULL,           offsetof(UnitStatusInfo, active_state)                      },
-                { "Documentation",                  "as",             NULL,           offsetof(UnitStatusInfo, documentation)                     },
+                { "LoadState",                      "s",               NULL,           offsetof(UnitStatusInfo, load_state)                        },
+                { "ActiveState",                    "s",               NULL,           offsetof(UnitStatusInfo, active_state)                      },
+                { "Documentation",                  "as",              NULL,           offsetof(UnitStatusInfo, documentation)                     },
                 {}
         }, status_map[] = {
-                { "Id",                             "s",              NULL,           offsetof(UnitStatusInfo, id)                                },
-                { "LoadState",                      "s",              NULL,           offsetof(UnitStatusInfo, load_state)                        },
-                { "ActiveState",                    "s",              NULL,           offsetof(UnitStatusInfo, active_state)                      },
-                { "SubState",                       "s",              NULL,           offsetof(UnitStatusInfo, sub_state)                         },
-                { "UnitFileState",                  "s",              NULL,           offsetof(UnitStatusInfo, unit_file_state)                   },
-                { "UnitFilePreset",                 "s",              NULL,           offsetof(UnitStatusInfo, unit_file_preset)                  },
-                { "Description",                    "s",              NULL,           offsetof(UnitStatusInfo, description)                       },
-                { "Following",                      "s",              NULL,           offsetof(UnitStatusInfo, following)                         },
-                { "Documentation",                  "as",             NULL,           offsetof(UnitStatusInfo, documentation)                     },
-                { "FragmentPath",                   "s",              NULL,           offsetof(UnitStatusInfo, fragment_path)                     },
-                { "SourcePath",                     "s",              NULL,           offsetof(UnitStatusInfo, source_path)                       },
-                { "ControlGroup",                   "s",              NULL,           offsetof(UnitStatusInfo, control_group)                     },
-                { "DropInPaths",                    "as",             NULL,           offsetof(UnitStatusInfo, dropin_paths)                      },
-                { "LoadError",                      "(ss)",           map_load_error, offsetof(UnitStatusInfo, load_error)                        },
-                { "Result",                         "s",              NULL,           offsetof(UnitStatusInfo, result)                            },
-                { "InactiveExitTimestamp",          "t",              NULL,           offsetof(UnitStatusInfo, inactive_exit_timestamp)           },
-                { "InactiveExitTimestampMonotonic", "t",              NULL,           offsetof(UnitStatusInfo, inactive_exit_timestamp_monotonic) },
-                { "ActiveEnterTimestamp",           "t",              NULL,           offsetof(UnitStatusInfo, active_enter_timestamp)            },
-                { "ActiveExitTimestamp",            "t",              NULL,           offsetof(UnitStatusInfo, active_exit_timestamp)             },
-                { "InactiveEnterTimestamp",         "t",              NULL,           offsetof(UnitStatusInfo, inactive_enter_timestamp)          },
-                { "NeedDaemonReload",               "b",              NULL,           offsetof(UnitStatusInfo, need_daemon_reload)                },
-                { "Transient",                      "b",              NULL,           offsetof(UnitStatusInfo, transient)                         },
-                { "ExecMainPID",                    "u",              NULL,           offsetof(UnitStatusInfo, main_pid)                          },
-                { "MainPID",                        "u",              map_main_pid,   0                                                           },
-                { "ControlPID",                     "u",              NULL,           offsetof(UnitStatusInfo, control_pid)                       },
-                { "StatusText",                     "s",              NULL,           offsetof(UnitStatusInfo, status_text)                       },
-                { "PIDFile",                        "s",              NULL,           offsetof(UnitStatusInfo, pid_file)                          },
-                { "StatusErrno",                    "i",              NULL,           offsetof(UnitStatusInfo, status_errno)                      },
-                { "ExecMainStartTimestamp",         "t",              NULL,           offsetof(UnitStatusInfo, start_timestamp)                   },
-                { "ExecMainExitTimestamp",          "t",              NULL,           offsetof(UnitStatusInfo, exit_timestamp)                    },
-                { "ExecMainCode",                   "i",              NULL,           offsetof(UnitStatusInfo, exit_code)                         },
-                { "ExecMainStatus",                 "i",              NULL,           offsetof(UnitStatusInfo, exit_status)                       },
-                { "ConditionTimestamp",             "t",              NULL,           offsetof(UnitStatusInfo, condition_timestamp)               },
-                { "ConditionResult",                "b",              NULL,           offsetof(UnitStatusInfo, condition_result)                  },
-                { "Conditions",                     "a(sbbsi)",       map_conditions, 0                                                           },
-                { "AssertTimestamp",                "t",              NULL,           offsetof(UnitStatusInfo, assert_timestamp)                  },
-                { "AssertResult",                   "b",              NULL,           offsetof(UnitStatusInfo, assert_result)                     },
-                { "Asserts",                        "a(sbbsi)",       map_asserts,    0                                                           },
-                { "NextElapseUSecRealtime",         "t",              NULL,           offsetof(UnitStatusInfo, next_elapse_real)                  },
-                { "NextElapseUSecMonotonic",        "t",              NULL,           offsetof(UnitStatusInfo, next_elapse_monotonic)             },
-                { "NAccepted",                      "u",              NULL,           offsetof(UnitStatusInfo, n_accepted)                        },
-                { "NConnections",                   "u",              NULL,           offsetof(UnitStatusInfo, n_connections)                     },
-                { "NRefused",                       "u",              NULL,           offsetof(UnitStatusInfo, n_refused)                         },
-                { "Accept",                         "b",              NULL,           offsetof(UnitStatusInfo, accept)                            },
-                { "Listen",                         "a(ss)",          map_listen,     offsetof(UnitStatusInfo, listen)                            },
-                { "SysFSPath",                      "s",              NULL,           offsetof(UnitStatusInfo, sysfs_path)                        },
-                { "Where",                          "s",              NULL,           offsetof(UnitStatusInfo, where)                             },
-                { "What",                           "s",              NULL,           offsetof(UnitStatusInfo, what)                              },
-                { "MemoryCurrent",                  "t",              NULL,           offsetof(UnitStatusInfo, memory_current)                    },
-                { "DefaultMemoryMin",               "t",              NULL,           offsetof(UnitStatusInfo, default_memory_min)                },
-                { "DefaultMemoryLow",               "t",              NULL,           offsetof(UnitStatusInfo, default_memory_low)                },
-                { "MemoryMin",                      "t",              NULL,           offsetof(UnitStatusInfo, memory_min)                        },
-                { "MemoryLow",                      "t",              NULL,           offsetof(UnitStatusInfo, memory_low)                        },
-                { "MemoryHigh",                     "t",              NULL,           offsetof(UnitStatusInfo, memory_high)                       },
-                { "MemoryMax",                      "t",              NULL,           offsetof(UnitStatusInfo, memory_max)                        },
-                { "MemorySwapMax",                  "t",              NULL,           offsetof(UnitStatusInfo, memory_swap_max)                   },
-                { "MemoryLimit",                    "t",              NULL,           offsetof(UnitStatusInfo, memory_limit)                      },
-                { "CPUUsageNSec",                   "t",              NULL,           offsetof(UnitStatusInfo, cpu_usage_nsec)                    },
-                { "TasksCurrent",                   "t",              NULL,           offsetof(UnitStatusInfo, tasks_current)                     },
-                { "TasksMax",                       "t",              NULL,           offsetof(UnitStatusInfo, tasks_max)                         },
-                { "IPIngressBytes",                 "t",              NULL,           offsetof(UnitStatusInfo, ip_ingress_bytes)                  },
-                { "IPEgressBytes",                  "t",              NULL,           offsetof(UnitStatusInfo, ip_egress_bytes)                   },
-                { "IOReadBytes",                    "t",              NULL,           offsetof(UnitStatusInfo, io_read_bytes)                     },
-                { "IOWriteBytes",                   "t",              NULL,           offsetof(UnitStatusInfo, io_write_bytes)                    },
-                { "ExecStartPre",                   "a(sasbttttuii)", map_exec,       0                                                           },
-                { "ExecStart",                      "a(sasbttttuii)", map_exec,       0                                                           },
-                { "ExecStartPost",                  "a(sasbttttuii)", map_exec,       0                                                           },
-                { "ExecReload",                     "a(sasbttttuii)", map_exec,       0                                                           },
-                { "ExecStopPre",                    "a(sasbttttuii)", map_exec,       0                                                           },
-                { "ExecStop",                       "a(sasbttttuii)", map_exec,       0                                                           },
-                { "ExecStopPost",                   "a(sasbttttuii)", map_exec,       0                                                           },
+                { "Id",                             "s",               NULL,           offsetof(UnitStatusInfo, id)                                },
+                { "LoadState",                      "s",               NULL,           offsetof(UnitStatusInfo, load_state)                        },
+                { "ActiveState",                    "s",               NULL,           offsetof(UnitStatusInfo, active_state)                      },
+                { "SubState",                       "s",               NULL,           offsetof(UnitStatusInfo, sub_state)                         },
+                { "UnitFileState",                  "s",               NULL,           offsetof(UnitStatusInfo, unit_file_state)                   },
+                { "UnitFilePreset",                 "s",               NULL,           offsetof(UnitStatusInfo, unit_file_preset)                  },
+                { "Description",                    "s",               NULL,           offsetof(UnitStatusInfo, description)                       },
+                { "Following",                      "s",               NULL,           offsetof(UnitStatusInfo, following)                         },
+                { "Documentation",                  "as",              NULL,           offsetof(UnitStatusInfo, documentation)                     },
+                { "FragmentPath",                   "s",               NULL,           offsetof(UnitStatusInfo, fragment_path)                     },
+                { "SourcePath",                     "s",               NULL,           offsetof(UnitStatusInfo, source_path)                       },
+                { "ControlGroup",                   "s",               NULL,           offsetof(UnitStatusInfo, control_group)                     },
+                { "DropInPaths",                    "as",              NULL,           offsetof(UnitStatusInfo, dropin_paths)                      },
+                { "LoadError",                      "(ss)",            map_load_error, offsetof(UnitStatusInfo, load_error)                        },
+                { "Result",                         "s",               NULL,           offsetof(UnitStatusInfo, result)                            },
+                { "InactiveExitTimestamp",          "t",               NULL,           offsetof(UnitStatusInfo, inactive_exit_timestamp)           },
+                { "InactiveExitTimestampMonotonic", "t",               NULL,           offsetof(UnitStatusInfo, inactive_exit_timestamp_monotonic) },
+                { "ActiveEnterTimestamp",           "t",               NULL,           offsetof(UnitStatusInfo, active_enter_timestamp)            },
+                { "ActiveExitTimestamp",            "t",               NULL,           offsetof(UnitStatusInfo, active_exit_timestamp)             },
+                { "InactiveEnterTimestamp",         "t",               NULL,           offsetof(UnitStatusInfo, inactive_enter_timestamp)          },
+                { "NeedDaemonReload",               "b",               NULL,           offsetof(UnitStatusInfo, need_daemon_reload)                },
+                { "Transient",                      "b",               NULL,           offsetof(UnitStatusInfo, transient)                         },
+                { "ExecMainPID",                    "u",               NULL,           offsetof(UnitStatusInfo, main_pid)                          },
+                { "MainPID",                        "u",               map_main_pid,   0                                                           },
+                { "ControlPID",                     "u",               NULL,           offsetof(UnitStatusInfo, control_pid)                       },
+                { "StatusText",                     "s",               NULL,           offsetof(UnitStatusInfo, status_text)                       },
+                { "PIDFile",                        "s",               NULL,           offsetof(UnitStatusInfo, pid_file)                          },
+                { "StatusErrno",                    "i",               NULL,           offsetof(UnitStatusInfo, status_errno)                      },
+                { "ExecMainStartTimestamp",         "t",               NULL,           offsetof(UnitStatusInfo, start_timestamp)                   },
+                { "ExecMainExitTimestamp",          "t",               NULL,           offsetof(UnitStatusInfo, exit_timestamp)                    },
+                { "ExecMainCode",                   "i",               NULL,           offsetof(UnitStatusInfo, exit_code)                         },
+                { "ExecMainStatus",                 "i",               NULL,           offsetof(UnitStatusInfo, exit_status)                       },
+                { "ConditionTimestamp",             "t",               NULL,           offsetof(UnitStatusInfo, condition_timestamp)               },
+                { "ConditionResult",                "b",               NULL,           offsetof(UnitStatusInfo, condition_result)                  },
+                { "Conditions",                     "a(sbbsi)",        map_conditions, 0                                                           },
+                { "AssertTimestamp",                "t",               NULL,           offsetof(UnitStatusInfo, assert_timestamp)                  },
+                { "AssertResult",                   "b",               NULL,           offsetof(UnitStatusInfo, assert_result)                     },
+                { "Asserts",                        "a(sbbsi)",        map_asserts,    0                                                           },
+                { "NextElapseUSecRealtime",         "t",               NULL,           offsetof(UnitStatusInfo, next_elapse_real)                  },
+                { "NextElapseUSecMonotonic",        "t",               NULL,           offsetof(UnitStatusInfo, next_elapse_monotonic)             },
+                { "NAccepted",                      "u",               NULL,           offsetof(UnitStatusInfo, n_accepted)                        },
+                { "NConnections",                   "u",               NULL,           offsetof(UnitStatusInfo, n_connections)                     },
+                { "NRefused",                       "u",               NULL,           offsetof(UnitStatusInfo, n_refused)                         },
+                { "Accept",                         "b",               NULL,           offsetof(UnitStatusInfo, accept)                            },
+                { "Listen",                         "a(ss)",           map_listen,     offsetof(UnitStatusInfo, listen)                            },
+                { "SysFSPath",                      "s",               NULL,           offsetof(UnitStatusInfo, sysfs_path)                        },
+                { "Where",                          "s",               NULL,           offsetof(UnitStatusInfo, where)                             },
+                { "What",                           "s",               NULL,           offsetof(UnitStatusInfo, what)                              },
+                { "MemoryCurrent",                  "t",               NULL,           offsetof(UnitStatusInfo, memory_current)                    },
+                { "DefaultMemoryMin",               "t",               NULL,           offsetof(UnitStatusInfo, default_memory_min)                },
+                { "DefaultMemoryLow",               "t",               NULL,           offsetof(UnitStatusInfo, default_memory_low)                },
+                { "MemoryMin",                      "t",               NULL,           offsetof(UnitStatusInfo, memory_min)                        },
+                { "MemoryLow",                      "t",               NULL,           offsetof(UnitStatusInfo, memory_low)                        },
+                { "MemoryHigh",                     "t",               NULL,           offsetof(UnitStatusInfo, memory_high)                       },
+                { "MemoryMax",                      "t",               NULL,           offsetof(UnitStatusInfo, memory_max)                        },
+                { "MemorySwapMax",                  "t",               NULL,           offsetof(UnitStatusInfo, memory_swap_max)                   },
+                { "MemoryLimit",                    "t",               NULL,           offsetof(UnitStatusInfo, memory_limit)                      },
+                { "CPUUsageNSec",                   "t",               NULL,           offsetof(UnitStatusInfo, cpu_usage_nsec)                    },
+                { "TasksCurrent",                   "t",               NULL,           offsetof(UnitStatusInfo, tasks_current)                     },
+                { "TasksMax",                       "t",               NULL,           offsetof(UnitStatusInfo, tasks_max)                         },
+                { "IPIngressBytes",                 "t",               NULL,           offsetof(UnitStatusInfo, ip_ingress_bytes)                  },
+                { "IPEgressBytes",                  "t",               NULL,           offsetof(UnitStatusInfo, ip_egress_bytes)                   },
+                { "IOReadBytes",                    "t",               NULL,           offsetof(UnitStatusInfo, io_read_bytes)                     },
+                { "IOWriteBytes",                   "t",               NULL,           offsetof(UnitStatusInfo, io_write_bytes)                    },
+                { "ExecStartPre",                   "a(sasbttttuii)",  map_exec,       0                                                           },
+                { "ExecStartPreEx",                 "a(sasasttttuii)", map_exec,       0                                                           },
+                { "ExecStart",                      "a(sasbttttuii)",  map_exec,       0                                                           },
+                { "ExecStartEx",                    "a(sasasttttuii)", map_exec,       0                                                           },
+                { "ExecStartPost",                  "a(sasbttttuii)",  map_exec,       0                                                           },
+                { "ExecStartPostEx",                "a(sasasttttuii)", map_exec,       0                                                           },
+                { "ExecReload",                     "a(sasbttttuii)",  map_exec,       0                                                           },
+                { "ExecStopPre",                    "a(sasbttttuii)",  map_exec,       0                                                           },
+                { "ExecStop",                       "a(sasbttttuii)",  map_exec,       0                                                           },
+                { "ExecStopPost",                   "a(sasbttttuii)",  map_exec,       0                                                           },
                 {}
         };
 
index f6f2e6317e55eb5e289a09bd079cfa6635cb4f8c..ec5189163473f0a46d9b04f0ccfed6b409f5c470 100644 (file)
@@ -402,6 +402,50 @@ static void test_error_catching(void) {
         assert_se(r == 42);
 }
 
+static void test_exec_command_flags_from_strv(void) {
+        ExecCommandFlags flags = 0;
+        char **valid_strv = STRV_MAKE("no-env-expand", "no-setuid", "ignore-failure");
+        char **invalid_strv = STRV_MAKE("no-env-expand", "no-setuid", "nonexistent-option", "ignore-failure");
+        int r;
+
+        r = exec_command_flags_from_strv(valid_strv, &flags);
+
+        assert_se(r == 0);
+        assert_se(FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND));
+        assert_se(FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID));
+        assert_se(FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
+        assert_se(!FLAGS_SET(flags, EXEC_COMMAND_AMBIENT_MAGIC));
+        assert_se(!FLAGS_SET(flags, EXEC_COMMAND_FULLY_PRIVILEGED));
+
+        r = exec_command_flags_from_strv(invalid_strv, &flags);
+
+        assert_se(r == -EINVAL);
+}
+
+static void test_exec_command_flags_to_strv(void) {
+        _cleanup_strv_free_ char **opts = NULL, **empty_opts = NULL, **invalid_opts = NULL;
+        ExecCommandFlags flags = 0;
+        int r;
+
+        flags |= (EXEC_COMMAND_AMBIENT_MAGIC|EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_IGNORE_FAILURE);
+
+        r = exec_command_flags_to_strv(flags, &opts);
+
+        assert_se(r == 0);
+        assert_se(strv_equal(opts, STRV_MAKE("ignore-failure", "ambient", "no-env-expand")));
+
+        r = exec_command_flags_to_strv(0, &empty_opts);
+
+        assert_se(r == 0);
+        assert_se(strv_equal(empty_opts, STRV_MAKE_EMPTY));
+
+        flags = _EXEC_COMMAND_FLAGS_INVALID;
+
+        r = exec_command_flags_to_strv(flags, &invalid_opts);
+
+        assert_se(r == -EINVAL);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
@@ -411,6 +455,8 @@ int main(int argc, char *argv[]) {
         test_stdout_gathering();
         test_environment_gathering();
         test_error_catching();
+        test_exec_command_flags_from_strv();
+        test_exec_command_flags_to_strv();
 
         return 0;
 }