socket: add SocketUser= and SocketGroup= for chown()ing sockets in the file system

This is relatively complex, as we cannot invoke NSS from PID 1, and thus
need to fork a helper process temporarily.
This commit is contained in:
Lennart Poettering 2014-06-05 09:55:53 +02:00
parent a8330cd118
commit 3900e5fdff
7 changed files with 253 additions and 95 deletions

View File

@ -372,16 +372,21 @@
</varlistentry>
<varlistentry>
<term><varname>DirectoryMode=</varname></term>
<listitem><para>If listening on a file
system socket or FIFO, the parent
directories are automatically created
if needed. This option specifies the
file system access mode used when
creating these directories. Takes an
access mode in octal
notation. Defaults to
0755.</para></listitem>
<term><varname>SocketUser=</varname></term>
<term><varname>SocketGroup=</varname></term>
<listitem><para>Takes a UNIX
user/group name. When specified
all AF_UNIX sockets and FIFO nodes in
the file system are owned by the
specified user and group. If unset
(the default), the nodes are owned by
the root user/group (if run in system
context) or the invoking user/group
(if run in user context). If only a
user is specified but no group, then
the group is derived from the user's
default group.</para></listitem>
</varlistentry>
<varlistentry>
@ -395,6 +400,19 @@
0666.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>DirectoryMode=</varname></term>
<listitem><para>If listening on a file
system socket or FIFO, the parent
directories are automatically created
if needed. This option specifies the
file system access mode used when
creating these directories. Takes an
access mode in octal
notation. Defaults to
0755.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Accept=</varname></term>
<listitem><para>Takes a boolean

View File

@ -91,8 +91,10 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("Backlog", "u", bus_property_get_unsigned, offsetof(Socket, backlog), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Socket, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BindToDevice", "s", NULL, offsetof(Socket, bind_to_device), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SocketUser", "s", NULL, offsetof(Socket, user), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SocketGroup", "s", NULL, offsetof(Socket, group), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Priority", "i", bus_property_get_int, offsetof(Socket, priority), SD_BUS_VTABLE_PROPERTY_CONST),

View File

@ -221,8 +221,10 @@ Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC
Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command)
Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command)
Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec)
Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
Socket.SocketUser, config_parse_unit_string_printf, 0, offsetof(Socket, user)
Socket.SocketGroup, config_parse_unit_string_printf, 0, offsetof(Socket, group)
Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)
Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections)
Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive)

View File

@ -55,6 +55,7 @@
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = UNIT_INACTIVE,
[SOCKET_START_PRE] = UNIT_ACTIVATING,
[SOCKET_START_CHOWN] = UNIT_ACTIVATING,
[SOCKET_START_POST] = UNIT_ACTIVATING,
[SOCKET_LISTENING] = UNIT_ACTIVE,
[SOCKET_RUNNING] = UNIT_ACTIVE,
@ -147,6 +148,9 @@ static void socket_done(Unit *u) {
strv_free(s->symlinks);
free(s->user);
free(s->group);
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
@ -592,6 +596,13 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sSmackLabelIPOut: %s\n",
prefix, s->smack_ip_out);
if (!isempty(s->user) || !isempty(s->group))
fprintf(f,
"%sOwnerUser: %s\n"
"%sOwnerGroup: %s\n",
prefix, strna(s->user),
prefix, strna(s->group));
LIST_FOREACH(port, p, s->ports) {
if (p->type == SOCKET_SOCKET) {
@ -1207,6 +1218,7 @@ static void socket_set_state(Socket *s, SocketState state) {
if (!IN_SET(state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
@ -1225,6 +1237,7 @@ static void socket_set_state(Socket *s, SocketState state) {
socket_unwatch_fds(s);
if (!IN_SET(state,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
SOCKET_RUNNING,
@ -1250,14 +1263,16 @@ static int socket_coldplug(Unit *u) {
if (s->deserialized_state == s->state)
return 0;
if (s->deserialized_state == SOCKET_START_PRE ||
s->deserialized_state == SOCKET_START_POST ||
s->deserialized_state == SOCKET_STOP_PRE ||
s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
s->deserialized_state == SOCKET_STOP_PRE_SIGKILL ||
s->deserialized_state == SOCKET_STOP_POST ||
s->deserialized_state == SOCKET_FINAL_SIGTERM ||
s->deserialized_state == SOCKET_FINAL_SIGKILL) {
if (IN_SET(s->deserialized_state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL,
SOCKET_STOP_POST,
SOCKET_FINAL_SIGTERM,
SOCKET_FINAL_SIGKILL)) {
if (s->control_pid <= 0)
return -EBADMSG;
@ -1271,12 +1286,14 @@ static int socket_coldplug(Unit *u) {
return r;
}
if (s->deserialized_state == SOCKET_START_POST ||
s->deserialized_state == SOCKET_LISTENING ||
s->deserialized_state == SOCKET_RUNNING ||
s->deserialized_state == SOCKET_STOP_PRE ||
s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
s->deserialized_state == SOCKET_STOP_PRE_SIGKILL) {
if (IN_SET(s->deserialized_state,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
SOCKET_RUNNING,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL)) {
r = socket_open_fds(s);
if (r < 0)
return r;
@ -1293,9 +1310,9 @@ static int socket_coldplug(Unit *u) {
}
static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
_cleanup_free_ char **argv = NULL;
pid_t pid;
int r;
char **argv;
assert(s);
assert(c);
@ -1333,22 +1350,100 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
s->exec_runtime,
&pid);
strv_free(argv);
if (r < 0)
goto fail;
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
/* FIXME: we need to do something here */
goto fail;
*_pid = pid;
return 0;
fail:
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
return r;
}
static int socket_chown(Socket *s, pid_t *_pid) {
pid_t pid;
int r;
r = socket_arm_timer(s);
if (r < 0)
goto fail;
/* We have to resolve the user names out-of-process, hence
* let's fork here. It's messy, but well, what can we do? */
pid = fork();
if (pid < 0)
return -errno;
if (pid == 0) {
SocketPort *p;
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
int ret;
default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1);
ignore_signals(SIGPIPE, -1);
log_forget_fds();
if (!isempty(s->user)) {
const char *user = s->user;
r = get_user_creds(&user, &uid, &gid, NULL, NULL);
if (r < 0) {
ret = EXIT_USER;
goto fail_child;
}
}
if (!isempty(s->group)) {
const char *group = s->group;
r = get_group_creds(&group, &gid);
if (r < 0) {
ret = EXIT_GROUP;
goto fail_child;
}
}
LIST_FOREACH(port, p, s->ports) {
const char *path;
if (p->type == SOCKET_SOCKET)
path = socket_address_get_path(&p->address);
else if (p->type == SOCKET_FIFO)
path = p->path;
if (!path)
continue;
if (chown(path, uid, gid) < 0) {
r = -errno;
ret = EXIT_CHOWN;
goto fail_child;
}
}
_exit(0);
fail_child:
log_open();
log_error("Failed to chown socket at step %s: %s", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD), strerror(-r));
_exit(ret);
}
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
goto fail;
*_pid = pid;
return 0;
fail:
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
return r;
}
@ -1376,11 +1471,12 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) {
s->result = f;
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_STOP_POST;
s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST];
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) {
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0)
goto fail;
socket_set_state(s, SOCKET_STOP_POST);
@ -1448,11 +1544,12 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) {
s->result = f;
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_STOP_PRE;
s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE];
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) {
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0)
goto fail;
socket_set_state(s, SOCKET_STOP_PRE);
@ -1487,17 +1584,11 @@ static void socket_enter_start_post(Socket *s) {
int r;
assert(s);
r = socket_open_fds(s);
if (r < 0) {
log_warning_unit(UNIT(s)->id, "%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r));
goto fail;
}
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_START_POST;
s->control_command = s->exec_command[SOCKET_EXEC_START_POST];
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) {
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0) {
log_warning_unit(UNIT(s)->id, "%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r));
@ -1514,27 +1605,61 @@ fail:
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
}
static void socket_enter_start_pre(Socket *s) {
static void socket_enter_start_chown(Socket *s) {
int r;
assert(s);
socket_unwatch_control_pid(s);
r = socket_open_fds(s);
if (r < 0) {
log_warning_unit(UNIT(s)->id, "%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r));
goto fail;
}
s->control_command_id = SOCKET_EXEC_START_PRE;
if (!isempty(s->user) || !isempty(s->group)) {
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0)
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_START_CHOWN;
s->control_command = NULL;
r = socket_chown(s, &s->control_pid);
if (r < 0) {
log_warning_unit(UNIT(s)->id, "%s failed to fork 'start-chown' task: %s", UNIT(s)->id, strerror(-r));
goto fail;
}
socket_set_state(s, SOCKET_START_PRE);
socket_set_state(s, SOCKET_START_CHOWN);
} else
socket_enter_start_post(s);
return;
fail:
log_warning_unit(UNIT(s)->id, "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r));
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
}
static void socket_enter_start_pre(Socket *s) {
int r;
assert(s);
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_START_PRE;
s->control_command = s->exec_command[SOCKET_EXEC_START_PRE];
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0) {
log_warning_unit(UNIT(s)->id, "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r));
goto fail;
}
socket_set_state(s, SOCKET_START_PRE);
} else
socket_enter_start_chown(s);
return;
fail:
socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
}
@ -1709,16 +1834,19 @@ static int socket_start(Unit *u) {
/* We cannot fulfill this request right now, try again later
* please! */
if (s->state == SOCKET_STOP_PRE ||
s->state == SOCKET_STOP_PRE_SIGKILL ||
s->state == SOCKET_STOP_PRE_SIGTERM ||
s->state == SOCKET_STOP_POST ||
s->state == SOCKET_FINAL_SIGTERM ||
s->state == SOCKET_FINAL_SIGKILL)
if (IN_SET(s->state,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGKILL,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_POST,
SOCKET_FINAL_SIGTERM,
SOCKET_FINAL_SIGKILL))
return -EAGAIN;
if (s->state == SOCKET_START_PRE ||
s->state == SOCKET_START_POST)
if (IN_SET(s->state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST))
return 0;
/* Cannot run this without the service being around */
@ -1764,18 +1892,21 @@ static int socket_stop(Unit *u) {
assert(s);
/* Already on it */
if (s->state == SOCKET_STOP_PRE ||
s->state == SOCKET_STOP_PRE_SIGTERM ||
s->state == SOCKET_STOP_PRE_SIGKILL ||
s->state == SOCKET_STOP_POST ||
s->state == SOCKET_FINAL_SIGTERM ||
s->state == SOCKET_FINAL_SIGKILL)
if (IN_SET(s->state,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL,
SOCKET_STOP_POST,
SOCKET_FINAL_SIGTERM,
SOCKET_FINAL_SIGKILL))
return 0;
/* If there's already something running we go directly into
* kill mode. */
if (s->state == SOCKET_START_PRE ||
s->state == SOCKET_START_POST) {
if (IN_SET(s->state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST)) {
socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS);
return -EAGAIN;
}
@ -2191,11 +2322,18 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case SOCKET_START_PRE:
if (f == SOCKET_SUCCESS)
socket_enter_start_post(s);
socket_enter_start_chown(s);
else
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f);
break;
case SOCKET_START_CHOWN:
if (f == SOCKET_SUCCESS)
socket_enter_start_post(s);
else
socket_enter_stop_pre(s, f);
break;
case SOCKET_START_POST:
if (f == SOCKET_SUCCESS)
socket_enter_listening(s);
@ -2233,65 +2371,53 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use
switch (s->state) {
case SOCKET_START_PRE:
log_warning_unit(UNIT(s)->id,
"%s starting timed out. Terminating.", UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s starting timed out. Terminating.", UNIT(s)->id);
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_START_CHOWN:
case SOCKET_START_POST:
log_warning_unit(UNIT(s)->id,
"%s starting timed out. Stopping.", UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s starting timed out. Stopping.", UNIT(s)->id);
socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_PRE:
log_warning_unit(UNIT(s)->id,
"%s stopping timed out. Terminating.", UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s stopping timed out. Terminating.", UNIT(s)->id);
socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_PRE_SIGTERM:
if (s->kill_context.send_sigkill) {
log_warning_unit(UNIT(s)->id,
"%s stopping timed out. Killing.", UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id);
socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT);
} else {
log_warning_unit(UNIT(s)->id,
"%s stopping timed out. Skipping SIGKILL. Ignoring.",
UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id);
socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
}
break;
case SOCKET_STOP_PRE_SIGKILL:
log_warning_unit(UNIT(s)->id,
"%s still around after SIGKILL. Ignoring.", UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id);
socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_POST:
log_warning_unit(UNIT(s)->id,
"%s stopping timed out (2). Terminating.", UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Terminating.", UNIT(s)->id);
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_FINAL_SIGTERM:
if (s->kill_context.send_sigkill) {
log_warning_unit(UNIT(s)->id,
"%s stopping timed out (2). Killing.", UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Killing.", UNIT(s)->id);
socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT);
} else {
log_warning_unit(UNIT(s)->id,
"%s stopping timed out (2). Skipping SIGKILL. Ignoring.",
UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Skipping SIGKILL. Ignoring.", UNIT(s)->id);
socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
}
break;
case SOCKET_FINAL_SIGKILL:
log_warning_unit(UNIT(s)->id,
"%s still around after SIGKILL (2). Entering failed mode.",
UNIT(s)->id);
log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL (2). Entering failed mode.", UNIT(s)->id);
socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
break;
@ -2440,6 +2566,7 @@ static int socket_get_timeout(Unit *u, uint64_t *timeout) {
static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = "dead",
[SOCKET_START_PRE] = "start-pre",
[SOCKET_START_CHOWN] = "start-chown",
[SOCKET_START_POST] = "start-post",
[SOCKET_LISTENING] = "listening",
[SOCKET_RUNNING] = "running",
@ -2456,6 +2583,7 @@ DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
[SOCKET_EXEC_START_PRE] = "StartPre",
[SOCKET_EXEC_START_CHOWN] = "StartChown",
[SOCKET_EXEC_START_POST] = "StartPost",
[SOCKET_EXEC_STOP_PRE] = "StopPre",
[SOCKET_EXEC_STOP_POST] = "StopPost"

View File

@ -32,6 +32,7 @@ typedef struct Socket Socket;
typedef enum SocketState {
SOCKET_DEAD,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
SOCKET_RUNNING,
@ -48,6 +49,7 @@ typedef enum SocketState {
typedef enum SocketExecCommand {
SOCKET_EXEC_START_PRE,
SOCKET_EXEC_START_CHOWN,
SOCKET_EXEC_START_POST,
SOCKET_EXEC_STOP_PRE,
SOCKET_EXEC_STOP_POST,
@ -157,6 +159,8 @@ struct Socket {
char *smack;
char *smack_ip_in;
char *smack_ip_out;
char *user, *group;
};
/* Called from the service code when collecting fds */

View File

@ -142,6 +142,9 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
case EXIT_RUNTIME_DIRECTORY:
return "RUNTIME_DIRECTORY";
case EXIT_CHOWN:
return "CHOWN";
}
}

View File

@ -74,7 +74,8 @@ typedef enum ExitStatus {
EXIT_PERSONALITY, /* 230 */
EXIT_APPARMOR_PROFILE,
EXIT_ADDRESS_FAMILIES,
EXIT_RUNTIME_DIRECTORY
EXIT_RUNTIME_DIRECTORY,
EXIT_CHOWN,
} ExitStatus;
typedef enum ExitStatusLevel {