summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/systemctl.xml6
-rw-r--r--man/systemd.exec.xml46
-rw-r--r--src/basic/terminal-util.h53
-rw-r--r--src/boot/bootctl.c5
-rw-r--r--src/core/dbus-execute.c86
-rw-r--r--src/core/execute.c86
-rw-r--r--src/core/execute.h5
-rw-r--r--src/core/load-fragment-gperf.gperf.m46
-rw-r--r--src/core/load-fragment.c104
-rw-r--r--src/core/load-fragment.h2
-rw-r--r--src/core/unit.c20
-rw-r--r--src/network/networkd-netdev-tunnel.c8
-rw-r--r--src/shared/install.c92
-rw-r--r--src/shared/install.h4
-rw-r--r--src/systemctl/systemctl.c106
-rw-r--r--src/test/test-install-root.c4
16 files changed, 491 insertions, 142 deletions
diff --git a/man/systemctl.xml b/man/systemctl.xml
index b51badf7fe..75885bcf02 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -544,8 +544,10 @@
<listitem>
<para>When used with
<command>enable</command>/<command>disable</command>/<command>is-enabled</command>
- (and related commands), use an alternate root path when
- looking for unit files.</para>
+ (and related commands), use the specified root path when looking for unit
+ files. If this option is present, <command>systemctl</command> will operate on
+ the file system directly, instead of communicating with the <command>systemd</command>
+ daemon to carry out changes.</para>
</listitem>
</varlistentry>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index a5a453031f..7453aa7bee 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -409,8 +409,9 @@
<option>null</option>,
<option>tty</option>,
<option>tty-force</option>,
- <option>tty-fail</option> or
- <option>socket</option>.</para>
+ <option>tty-fail</option>,
+ <option>socket</option> or
+ <option>fd</option>.</para>
<para>If <option>null</option> is selected, standard input
will be connected to <filename>/dev/null</filename>, i.e. all
@@ -448,6 +449,20 @@
<citerefentry project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
daemon.</para>
+ <para>The <option>fd</option> option connects
+ the input stream to a single file descriptor provided by a socket unit.
+ A custom named file descriptor can be specified as part of this option,
+ after a <literal>:</literal> (e.g. <literal>fd:<replaceable>foobar</replaceable></literal>).
+ If no name is specified, <literal>stdin</literal> is assumed
+ (i.e. <literal>fd</literal> is equivalent to <literal>fd:stdin</literal>).
+ At least one socket unit defining such name must be explicitly provided via the
+ <varname>Sockets=</varname> option, and file descriptor name may differ
+ from the name of its containing socket unit.
+ If multiple matches are found, the first one will be used.
+ See <varname>FileDescriptorName=</varname> in
+ <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for more details about named descriptors and ordering.</para>
+
<para>This setting defaults to
<option>null</option>.</para></listitem>
</varlistentry>
@@ -464,8 +479,9 @@
<option>kmsg</option>,
<option>journal+console</option>,
<option>syslog+console</option>,
- <option>kmsg+console</option> or
- <option>socket</option>.</para>
+ <option>kmsg+console</option>,
+ <option>socket</option> or
+ <option>fd</option>.</para>
<para><option>inherit</option> duplicates the file descriptor
of standard input for standard output.</para>
@@ -514,6 +530,20 @@
similar to the same option of
<varname>StandardInput=</varname>.</para>
+ <para>The <option>fd</option> option connects
+ the output stream to a single file descriptor provided by a socket unit.
+ A custom named file descriptor can be specified as part of this option,
+ after a <literal>:</literal> (e.g. <literal>fd:<replaceable>foobar</replaceable></literal>).
+ If no name is specified, <literal>stdout</literal> is assumed
+ (i.e. <literal>fd</literal> is equivalent to <literal>fd:stdout</literal>).
+ At least one socket unit defining such name must be explicitly provided via the
+ <varname>Sockets=</varname> option, and file descriptor name may differ
+ from the name of its containing socket unit.
+ If multiple matches are found, the first one will be used.
+ See <varname>FileDescriptorName=</varname> in
+ <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for more details about named descriptors and ordering.</para>
+
<para>If the standard output (or error output, see below) of a unit is connected to the journal, syslog or the
kernel log buffer, the unit will implicitly gain a dependency of type <varname>After=</varname> on
<filename>systemd-journald.socket</filename> (also see the automatic dependencies section above).</para>
@@ -531,9 +561,13 @@
<listitem><para>Controls where file descriptor 2 (STDERR) of
the executed processes is connected to. The available options
are identical to those of <varname>StandardOutput=</varname>,
- with one exception: if set to <option>inherit</option> the
+ with some exceptions: if set to <option>inherit</option> the
file descriptor used for standard output is duplicated for
- standard error. This setting defaults to the value set with
+ standard error, while <option>fd</option> operates on the error
+ stream and will look by default for a descriptor named
+ <literal>stderr</literal>.</para>
+
+ <para>This setting defaults to the value set with
<option>DefaultStandardError=</option> in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
which defaults to <option>inherit</option>. Note that setting
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index 169ab772ff..b862bfaf05 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -36,6 +36,10 @@
#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m"
#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m"
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
+#define ANSI_HIGHLIGHT_RED_UNDERLINE "\x1B[0;1;4;31m"
+#define ANSI_HIGHLIGHT_GREEN_UNDERLINE "\x1B[0;1;4;32m"
+#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE "\x1B[0;1;4;33m"
+#define ANSI_HIGHLIGHT_BLUE_UNDERLINE "\x1B[0;1;4;34m"
#define ANSI_NORMAL "\x1B[0m"
#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
@@ -83,37 +87,24 @@ bool on_tty(void);
bool terminal_is_dumb(void);
bool colors_enabled(void);
-static inline const char *ansi_underline(void) {
- return colors_enabled() ? ANSI_UNDERLINE : "";
-}
-
-static inline const char *ansi_highlight(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT : "";
-}
-
-static inline const char *ansi_highlight_underline(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_UNDERLINE : "";
-}
-
-static inline const char *ansi_highlight_red(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_RED : "";
-}
-
-static inline const char *ansi_highlight_green(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_GREEN : "";
-}
-
-static inline const char *ansi_highlight_yellow(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_YELLOW : "";
-}
-
-static inline const char *ansi_highlight_blue(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_BLUE : "";
-}
-
-static inline const char *ansi_normal(void) {
- return colors_enabled() ? ANSI_NORMAL : "";
-}
+#define DEFINE_ANSI_FUNC(name, NAME) \
+ static inline const char *ansi_##name(void) { \
+ return colors_enabled() ? ANSI_##NAME : ""; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+DEFINE_ANSI_FUNC(underline, UNDERLINE);
+DEFINE_ANSI_FUNC(highlight, HIGHLIGHT);
+DEFINE_ANSI_FUNC(highlight_underline, HIGHLIGHT_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
+DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
+DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW);
+DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
+DEFINE_ANSI_FUNC(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE);
+DEFINE_ANSI_FUNC(normal, NORMAL);
int get_ctty_devnr(pid_t pid, dev_t *d);
int get_ctty(pid_t, dev_t *_devnr, char **r);
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index ee6d7eb864..dc11b0d9db 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -621,7 +621,8 @@ static const char *efi_subdirs[] = {
"EFI/systemd",
"EFI/BOOT",
"loader",
- "loader/entries"
+ "loader/entries",
+ NULL
};
static int create_dirs(const char *esp_path) {
@@ -917,7 +918,7 @@ static int remove_binaries(const char *esp_path) {
if (q < 0 && r == 0)
r = q;
- for (i = ELEMENTSOF(efi_subdirs); i > 0; i--) {
+ for (i = ELEMENTSOF(efi_subdirs)-1; i > 0; i--) {
q = rmdir_one(esp_path, efi_subdirs[i-1]);
if (q < 0 && r == 0)
r = q;
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index b8720d7d3d..1a7f770db1 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -627,6 +627,53 @@ static int property_get_syslog_facility(
return sd_bus_message_append(reply, "i", LOG_FAC(c->syslog_priority));
}
+static int property_get_input_fdname(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+ const char *name;
+
+ assert(bus);
+ assert(c);
+ assert(property);
+ assert(reply);
+
+ name = exec_context_fdname(c, STDIN_FILENO);
+
+ return sd_bus_message_append(reply, "s", name);
+}
+
+static int property_get_output_fdname(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+ const char *name = NULL;
+
+ assert(bus);
+ assert(c);
+ assert(property);
+ assert(reply);
+
+ if (c->std_output == EXEC_OUTPUT_NAMED_FD && streq(property, "StandardOutputFileDescriptorName"))
+ name = exec_context_fdname(c, STDOUT_FILENO);
+ else if (c->std_error == EXEC_OUTPUT_NAMED_FD && streq(property, "StandardErrorFileDescriptorName"))
+ name = exec_context_fdname(c, STDERR_FILENO);
+
+ return sd_bus_message_append(reply, "s", name);
+}
+
const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -677,8 +724,11 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("CPUSchedulingResetOnFork", "b", bus_property_get_bool, offsetof(ExecContext, cpu_sched_reset_on_fork), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NonBlocking", "b", bus_property_get_bool, offsetof(ExecContext, non_blocking), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StandardInput", "s", property_get_exec_input, offsetof(ExecContext, std_input), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StandardInputFileDescriptorName", "s", property_get_input_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StandardOutput", "s", bus_property_get_exec_output, offsetof(ExecContext, std_output), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StandardOutputFileDescriptorName", "s", property_get_output_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StandardError", "s", bus_property_get_exec_output, offsetof(ExecContext, std_error), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StandardErrorFileDescriptorName", "s", property_get_output_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TTYPath", "s", NULL, offsetof(ExecContext, tty_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TTYReset", "b", bus_property_get_bool, offsetof(ExecContext, tty_reset), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TTYVHangup", "b", bus_property_get_bool, offsetof(ExecContext, tty_vhangup), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1030,7 +1080,6 @@ int bus_exec_context_set_transient_property(
return 1;
-
} else if (streq(name, "StandardOutput")) {
const char *s;
ExecOutput p;
@@ -1072,6 +1121,41 @@ int bus_exec_context_set_transient_property(
return 1;
} else if (STR_IN_SET(name,
+ "StandardInputFileDescriptorName", "StandardOutputFileDescriptorName", "StandardErrorFileDescriptorName")) {
+ const char *s;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ if (!fdname_is_valid(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name");
+
+ if (mode != UNIT_CHECK) {
+ if (streq(name, "StandardInputFileDescriptorName")) {
+ c->std_input = EXEC_INPUT_NAMED_FD;
+ r = free_and_strdup(&c->stdio_fdname[STDIN_FILENO], s);
+ if (r < 0)
+ return r;
+ unit_write_drop_in_private_format(u, mode, name, "StandardInput=fd:%s", s);
+ } else if (streq(name, "StandardOutputFileDescriptorName")) {
+ c->std_output = EXEC_OUTPUT_NAMED_FD;
+ r = free_and_strdup(&c->stdio_fdname[STDOUT_FILENO], s);
+ if (r < 0)
+ return r;
+ unit_write_drop_in_private_format(u, mode, name, "StandardOutput=fd:%s", s);
+ } else if (streq(name, "StandardErrorFileDescriptorName")) {
+ c->std_error = EXEC_OUTPUT_NAMED_FD;
+ r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], s);
+ if (r < 0)
+ return r;
+ unit_write_drop_in_private_format(u, mode, name, "StandardError=fd:%s", s);
+ }
+ }
+
+ return 1;
+
+ } else if (STR_IN_SET(name,
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
diff --git a/src/core/execute.c b/src/core/execute.c
index 59c2bd0dd2..1b7b4a928d 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -411,7 +411,8 @@ static int fixup_output(ExecOutput std_output, int socket_fd) {
static int setup_input(
const ExecContext *context,
const ExecParameters *params,
- int socket_fd) {
+ int socket_fd,
+ int named_iofds[3]) {
ExecInput i;
@@ -461,6 +462,10 @@ static int setup_input(
case EXEC_INPUT_SOCKET:
return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
+ case EXEC_INPUT_NAMED_FD:
+ (void) fd_nonblock(named_iofds[STDIN_FILENO], false);
+ return dup2(named_iofds[STDIN_FILENO], STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
+
default:
assert_not_reached("Unknown input type");
}
@@ -472,6 +477,7 @@ static int setup_output(
const ExecParameters *params,
int fileno,
int socket_fd,
+ int named_iofds[3],
const char *ident,
uid_t uid,
gid_t gid,
@@ -523,7 +529,7 @@ static int setup_output(
return fileno;
/* Duplicate from stdout if possible */
- if (e == o || e == EXEC_OUTPUT_INHERIT)
+ if ((e == o && e != EXEC_OUTPUT_NAMED_FD) || e == EXEC_OUTPUT_INHERIT)
return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
o = e;
@@ -585,6 +591,10 @@ static int setup_output(
assert(socket_fd >= 0);
return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
+ case EXEC_OUTPUT_NAMED_FD:
+ (void) fd_nonblock(named_iofds[fileno], false);
+ return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno;
+
default:
assert_not_reached("Unknown error type");
}
@@ -2157,6 +2167,7 @@ static int exec_child(
DynamicCreds *dcreds,
char **argv,
int socket_fd,
+ int named_iofds[3],
int *fds, unsigned n_fds,
char **files_env,
int user_lookup_fd,
@@ -2298,19 +2309,19 @@ static int exec_child(
if (socket_fd >= 0)
(void) fd_nonblock(socket_fd, false);
- r = setup_input(context, params, socket_fd);
+ r = setup_input(context, params, socket_fd, named_iofds);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
- r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
+ r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return r;
}
- r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
+ r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDERR;
return r;
@@ -2829,6 +2840,7 @@ int exec_spawn(Unit *unit,
int *fds = NULL; unsigned n_fds = 0;
_cleanup_free_ char *line = NULL;
int socket_fd, r;
+ int named_iofds[3] = { -1, -1, -1 };
char **argv;
pid_t pid;
@@ -2855,6 +2867,10 @@ int exec_spawn(Unit *unit,
n_fds = params->n_fds;
}
+ r = exec_context_named_iofds(unit, context, params, named_iofds);
+ if (r < 0)
+ return log_unit_error_errno(unit, r, "Failed to load a named file descriptor: %m");
+
r = exec_context_load_environment(unit, context, &files_env);
if (r < 0)
return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
@@ -2884,6 +2900,7 @@ int exec_spawn(Unit *unit,
dcreds,
argv,
socket_fd,
+ named_iofds,
fds, n_fds,
files_env,
unit->manager->user_lookup_fds[1],
@@ -2946,6 +2963,9 @@ void exec_context_done(ExecContext *c) {
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
c->rlimit[l] = mfree(c->rlimit[l]);
+ for (l = 0; l < 3; l++)
+ c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
+
c->working_directory = mfree(c->working_directory);
c->root_directory = mfree(c->root_directory);
c->tty_path = mfree(c->tty_path);
@@ -3044,6 +3064,56 @@ static void invalid_env(const char *p, void *userdata) {
log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
}
+const char* exec_context_fdname(const ExecContext *c, int fd_index) {
+ assert(c);
+
+ switch (fd_index) {
+ case STDIN_FILENO:
+ if (c->std_input != EXEC_INPUT_NAMED_FD)
+ return NULL;
+ return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
+ case STDOUT_FILENO:
+ if (c->std_output != EXEC_OUTPUT_NAMED_FD)
+ return NULL;
+ return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
+ case STDERR_FILENO:
+ if (c->std_error != EXEC_OUTPUT_NAMED_FD)
+ return NULL;
+ return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
+ default:
+ return NULL;
+ }
+}
+
+int exec_context_named_iofds(Unit *unit, const ExecContext *c, const ExecParameters *p, int named_iofds[3]) {
+ unsigned i, targets;
+ const char *stdio_fdname[3];
+
+ assert(c);
+ assert(p);
+
+ targets = (c->std_input == EXEC_INPUT_NAMED_FD) +
+ (c->std_output == EXEC_OUTPUT_NAMED_FD) +
+ (c->std_error == EXEC_OUTPUT_NAMED_FD);
+
+ for (i = 0; i < 3; i++)
+ stdio_fdname[i] = exec_context_fdname(c, i);
+
+ for (i = 0; i < p->n_fds && targets > 0; i++)
+ if (named_iofds[STDIN_FILENO] < 0 && c->std_input == EXEC_INPUT_NAMED_FD && stdio_fdname[STDIN_FILENO] && streq(p->fd_names[i], stdio_fdname[STDIN_FILENO])) {
+ named_iofds[STDIN_FILENO] = p->fds[i];
+ targets--;
+ } else if (named_iofds[STDOUT_FILENO] < 0 && c->std_output == EXEC_OUTPUT_NAMED_FD && stdio_fdname[STDOUT_FILENO] && streq(p->fd_names[i], stdio_fdname[STDOUT_FILENO])) {
+ named_iofds[STDOUT_FILENO] = p->fds[i];
+ targets--;
+ } else if (named_iofds[STDERR_FILENO] < 0 && c->std_error == EXEC_OUTPUT_NAMED_FD && stdio_fdname[STDERR_FILENO] && streq(p->fd_names[i], stdio_fdname[STDERR_FILENO])) {
+ named_iofds[STDERR_FILENO] = p->fds[i];
+ targets--;
+ }
+
+ return (targets == 0 ? 0 : -ENOENT);
+}
+
int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
char **i, **r = NULL;
@@ -3896,7 +3966,8 @@ static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
[EXEC_INPUT_TTY] = "tty",
[EXEC_INPUT_TTY_FORCE] = "tty-force",
[EXEC_INPUT_TTY_FAIL] = "tty-fail",
- [EXEC_INPUT_SOCKET] = "socket"
+ [EXEC_INPUT_SOCKET] = "socket",
+ [EXEC_INPUT_NAMED_FD] = "fd",
};
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
@@ -3911,7 +3982,8 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
[EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
[EXEC_OUTPUT_JOURNAL] = "journal",
[EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
- [EXEC_OUTPUT_SOCKET] = "socket"
+ [EXEC_OUTPUT_SOCKET] = "socket",
+ [EXEC_OUTPUT_NAMED_FD] = "fd",
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
diff --git a/src/core/execute.h b/src/core/execute.h
index 1de439c3ad..c7d0f7761e 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -50,6 +50,7 @@ typedef enum ExecInput {
EXEC_INPUT_TTY_FORCE,
EXEC_INPUT_TTY_FAIL,
EXEC_INPUT_SOCKET,
+ EXEC_INPUT_NAMED_FD,
_EXEC_INPUT_MAX,
_EXEC_INPUT_INVALID = -1
} ExecInput;
@@ -65,6 +66,7 @@ typedef enum ExecOutput {
EXEC_OUTPUT_JOURNAL,
EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
EXEC_OUTPUT_SOCKET,
+ EXEC_OUTPUT_NAMED_FD,
_EXEC_OUTPUT_MAX,
_EXEC_OUTPUT_INVALID = -1
} ExecOutput;
@@ -120,6 +122,7 @@ struct ExecContext {
ExecInput std_input;
ExecOutput std_output;
ExecOutput std_error;
+ char *stdio_fdname[3];
nsec_t timer_slack_nsec;
@@ -284,6 +287,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_root);
int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l);
+int exec_context_named_iofds(Unit *unit, const ExecContext *c, const ExecParameters *p, int named_iofds[3]);
+const char* exec_context_fdname(const ExecContext *c, int fd_index);
bool exec_context_may_touch_console(ExecContext *c);
bool exec_context_maintains_privileges(ExecContext *c);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index a700d853cc..08c88b6b53 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -35,9 +35,9 @@ $1.Environment, config_parse_environ, 0,
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
$1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment)
$1.DynamicUser, config_parse_bool, 0, offsetof($1, exec_context.dynamic_user)
-$1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input)
-$1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output)
-$1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error)
+$1.StandardInput, config_parse_exec_input, 0, offsetof($1, exec_context)
+$1.StandardOutput, config_parse_exec_output, 0, offsetof($1, exec_context)
+$1.StandardError, config_parse_exec_output, 0, offsetof($1, exec_context)
$1.TTYPath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.tty_path)
$1.TTYReset, config_parse_bool, 0, offsetof($1, exec_context.tty_reset)
$1.TTYVHangup, config_parse_bool, 0, offsetof($1, exec_context.tty_vhangup)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 8cc7a8e765..a69f60097d 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -776,8 +776,104 @@ int config_parse_socket_bindtodevice(
return 0;
}
-DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input literal specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output literal specifier");
+
+int config_parse_exec_input(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ ExecContext *c = data;
+ const char *name;
+ int r;
+
+ assert(data);
+ assert(filename);
+ assert(line);
+ assert(rvalue);
+
+ name = startswith(rvalue, "fd:");
+ if (name) {
+ /* Strip prefix and validate fd name */
+ if (!fdname_is_valid(name)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", name);
+ return 0;
+ }
+ c->std_input = EXEC_INPUT_NAMED_FD;
+ r = free_and_strdup(&c->stdio_fdname[STDIN_FILENO], name);
+ if (r < 0)
+ log_oom();
+ return r;
+ } else {
+ ExecInput ei = exec_input_from_string(rvalue);
+ if (ei == _EXEC_INPUT_INVALID)
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse input specifier, ignoring: %s", rvalue);
+ else
+ c->std_input = ei;
+ return 0;
+ }
+}
+
+int config_parse_exec_output(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ ExecContext *c = data;
+ ExecOutput eo;
+ const char *name;
+ int r;
+
+ assert(data);
+ assert(filename);
+ assert(line);
+ assert(lvalue);
+ assert(rvalue);
+
+ name = startswith(rvalue, "fd:");
+ if (name) {
+ /* Strip prefix and validate fd name */
+ if (!fdname_is_valid(name)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", name);
+ return 0;
+ }
+ eo = EXEC_OUTPUT_NAMED_FD;
+ } else {
+ eo = exec_output_from_string(rvalue);
+ if (eo == _EXEC_OUTPUT_INVALID) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output specifier, ignoring: %s", rvalue);
+ return 0;
+ }
+ }
+
+ if (streq(lvalue, "StandardOutput")) {
+ c->std_output = eo;
+ r = free_and_strdup(&c->stdio_fdname[STDOUT_FILENO], name);
+ if (r < 0)
+ log_oom();
+ return r;
+ } else if (streq(lvalue, "StandardError")) {
+ c->std_error = eo;
+ r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], name);
+ if (r < 0)
+ log_oom();
+ return r;
+ } else {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output property, ignoring: %s", lvalue);
+ return 0;
+ }
+}
int config_parse_exec_io_class(const char *unit,
const char *filename,
@@ -4183,8 +4279,8 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_exec_cpu_affinity, "CPUAFFINITY" },
{ config_parse_mode, "MODE" },
{ config_parse_unit_env_file, "FILE" },
- { config_parse_output, "OUTPUT" },
- { config_parse_input, "INPUT" },
+ { config_parse_exec_output, "OUTPUT" },
+ { config_parse_exec_input, "INPUT" },
{ config_parse_log_facility, "FACILITY" },
{ config_parse_log_level, "LEVEL" },
{ config_parse_exec_secure_bits, "SECUREBITS" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 8b688740cf..6d1fe55bcd 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -45,7 +45,9 @@ int config_parse_service_timeout(const char *unit, const char *filename, unsigne
int config_parse_service_type(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_service_restart(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_socket_bindtodevice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_output(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_output(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_input(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_input(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_io_class(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_io_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/core/unit.c b/src/core/unit.c
index b24ca5aed8..2fa397bd41 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -858,18 +858,14 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return r;
}
- if (c->std_output != EXEC_OUTPUT_KMSG &&
- c->std_output != EXEC_OUTPUT_SYSLOG &&
- c->std_output != EXEC_OUTPUT_JOURNAL &&
- c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
- c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE &&
- c->std_output != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
- c->std_error != EXEC_OUTPUT_KMSG &&
- c->std_error != EXEC_OUTPUT_SYSLOG &&
- c->std_error != EXEC_OUTPUT_JOURNAL &&
- c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
- c->std_error != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
- c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE)
+ if (!IN_SET(c->std_output,
+ EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
+ EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE,
+ EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) &&
+ !IN_SET(c->std_error,
+ EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
+ EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE,
+ EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE))
return 0;
/* If syslog or kernel logging is requested, make sure our own
diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c
index 77a4734df8..9138ee4511 100644
--- a/src/network/networkd-netdev-tunnel.c
+++ b/src/network/networkd-netdev-tunnel.c
@@ -201,12 +201,18 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl
}
static int netdev_vti_fill_message_key(NetDev *netdev, Link *link, sd_netlink_message *m) {
- Tunnel *t = VTI(netdev);
uint32_t ikey, okey;
+ Tunnel *t;
int r;
assert(link);
assert(m);
+
+ if (netdev->kind == NETDEV_KIND_VTI)
+ t = VTI(netdev);
+ else
+ t = VTI6(netdev);
+
assert(t);
if (t->key != 0)
diff --git a/src/shared/install.c b/src/shared/install.c
index f70b3e3dd5..d33a658d0a 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -934,11 +934,14 @@ static int install_info_may_process(
/**
* Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process
* hashmap, or retrieves the existing one if already present.
+ *
+ * Returns negative on error, 0 if the unit was already known, 1 otherwise.
*/
static int install_info_add(
InstallContext *c,
const char *name,
const char *path,
+ bool auxiliary,
UnitFileInstallInfo **ret) {
UnitFileInstallInfo *i = NULL;
@@ -955,6 +958,8 @@ static int install_info_add(
i = install_info_find(c, name);
if (i) {
+ i->auxiliary = i->auxiliary && auxiliary;
+
if (ret)
*ret = i;
return 0;
@@ -968,6 +973,7 @@ static int install_info_add(
if (!i)
return -ENOMEM;
i->type = _UNIT_FILE_TYPE_INVALID;
+ i->auxiliary = auxiliary;
i->name = strdup(name);
if (!i->name) {
@@ -990,7 +996,7 @@ static int install_info_add(
if (ret)
*ret = i;
- return 0;
+ return 1;
fail:
install_info_free(i);
@@ -1039,7 +1045,7 @@ static int config_parse_also(
void *data,
void *userdata) {
- UnitFileInstallInfo *i = userdata;
+ UnitFileInstallInfo *info = userdata, *alsoinfo = NULL;
InstallContext *c = data;
int r;
@@ -1048,7 +1054,7 @@ static int config_parse_also(
assert(rvalue);
for (;;) {
- _cleanup_free_ char *word = NULL;
+ _cleanup_free_ char *word = NULL, *printed = NULL;
r = extract_first_word(&rvalue, &word, NULL, 0);
if (r < 0)
@@ -1056,15 +1062,22 @@ static int config_parse_also(
if (r == 0)
break;
- r = install_info_add(c, word, NULL, NULL);
+ r = install_full_printf(info, word, &printed);
+ if (r < 0)
+ return r;
+
+ if (!unit_name_is_valid(printed, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ r = install_info_add(c, printed, NULL, true, &alsoinfo);
if (r < 0)
return r;
- r = strv_push(&i->also, word);
+ r = strv_push(&info->also, printed);
if (r < 0)
return r;
- word = NULL;
+ printed = NULL;
}
return 0;
@@ -1187,7 +1200,7 @@ static int unit_file_load(
config_item_table_lookup, items,
true, true, false, info);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to parse %s: %m", info->name);
info->type = UNIT_FILE_TYPE_REGULAR;
@@ -1425,7 +1438,7 @@ static int install_info_traverse(
bn = buffer;
}
- r = install_info_add(c, bn, NULL, &i);
+ r = install_info_add(c, bn, NULL, false, &i);
if (r < 0)
return r;
@@ -1464,9 +1477,9 @@ static int install_info_add_auto(
pp = prefix_roota(paths->root_dir, name_or_path);
- return install_info_add(c, NULL, pp, ret);
+ return install_info_add(c, NULL, pp, false, ret);
} else
- return install_info_add(c, name_or_path, NULL, ret);
+ return install_info_add(c, name_or_path, NULL, false, ret);
}
static int install_info_discover(
@@ -1475,7 +1488,9 @@ static int install_info_discover(
const LookupPaths *paths,
const char *name,
SearchFlags flags,
- UnitFileInstallInfo **ret) {
+ UnitFileInstallInfo **ret,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
UnitFileInstallInfo *i;
int r;
@@ -1485,10 +1500,12 @@ static int install_info_discover(
assert(name);
r = install_info_add_auto(c, paths, name, &i);
- if (r < 0)
- return r;
+ if (r >= 0)
+ r = install_info_traverse(scope, c, paths, i, flags, ret);
- return install_info_traverse(scope, c, paths, i, flags, ret);
+ if (r < 0)
+ unit_file_changes_add(changes, n_changes, r, name, NULL);
+ return r;
}
static int install_info_symlink_alias(
@@ -1700,8 +1717,10 @@ static int install_context_apply(
return q;
r = install_info_traverse(scope, c, paths, i, flags, NULL);
- if (r < 0)
+ if (r < 0) {
+ unit_file_changes_add(changes, n_changes, r, i->name, NULL);
return r;
+ }
/* We can attempt to process a masked unit when a different unit
* that we were processing specifies it in Also=. */
@@ -1759,10 +1778,15 @@ static int install_context_mark_for_removal(
return r;
r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
- if (r == -ENOLINK)
- return 0;
- else if (r < 0)
- return r;
+ if (r == -ENOLINK) {
+ log_debug_errno(r, "Name %s leads to a dangling symlink, ignoring.", i->name);
+ continue;
+ } else if (r == -ENOENT && i->auxiliary) {
+ /* some unit specified in Also= or similar is missing */
+ log_debug_errno(r, "Auxiliary unit %s not found, ignoring.", i->name);
+ continue;
+ } else if (r < 0)
+ return log_debug_errno(r, "Failed to find unit %s: %m", i->name);
if (i->type != UNIT_FILE_TYPE_REGULAR) {
log_debug("Unit %s has type %s, ignoring.",
@@ -2198,7 +2222,8 @@ int unit_file_add_dependency(
config_path = runtime ? paths.runtime_config : paths.persistent_config;
- r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info);
+ r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &target_info, changes, n_changes);
if (r < 0)
return r;
r = install_info_may_process(target_info, &paths, changes, n_changes);
@@ -2210,7 +2235,8 @@ int unit_file_add_dependency(
STRV_FOREACH(f, files) {
char ***l;
- r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
r = install_info_may_process(i, &paths, changes, n_changes);
@@ -2263,7 +2289,8 @@ int unit_file_enable(
config_path = runtime ? paths.runtime_config : paths.persistent_config;
STRV_FOREACH(f, files) {
- r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
r = install_info_may_process(i, &paths, changes, n_changes);
@@ -2309,7 +2336,7 @@ int unit_file_disable(
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
- r = install_info_add(&c, *i, NULL, NULL);
+ r = install_info_add(&c, *i, NULL, false, NULL);
if (r < 0)
return r;
}
@@ -2376,7 +2403,7 @@ int unit_file_set_default(
if (r < 0)
return r;
- r = install_info_discover(scope, &c, &paths, name, 0, &i);
+ r = install_info_discover(scope, &c, &paths, name, 0, &i, changes, n_changes);
if (r < 0)
return r;
r = install_info_may_process(i, &paths, changes, n_changes);
@@ -2406,7 +2433,8 @@ int unit_file_get_default(
if (r < 0)
return r;
- r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, NULL, NULL);
if (r < 0)
return r;
r = install_info_may_process(i, &paths, NULL, 0);
@@ -2438,7 +2466,8 @@ static int unit_file_lookup_state(
if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
- r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, NULL, NULL);
if (r < 0)
return r;
@@ -2525,7 +2554,7 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *
if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
- r = install_info_discover(scope, &c, paths, name, 0, NULL);
+ r = install_info_discover(scope, &c, paths, name, 0, NULL, NULL, NULL);
if (r == -ENOENT)
return 0;
if (r < 0)
@@ -2743,7 +2772,8 @@ static int preset_prepare_one(
if (install_info_find(plus, name) || install_info_find(minus, name))
return 0;
- r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
if (!streq(name, i->name)) {
@@ -2756,7 +2786,8 @@ static int preset_prepare_one(
return r;
if (r > 0) {
- r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
@@ -2764,7 +2795,8 @@ static int preset_prepare_one(
if (r < 0)
return r;
} else
- r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
return r;
}
diff --git a/src/shared/install.h b/src/shared/install.h
index c6aa4f6ef1..b1f220693b 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -119,10 +119,10 @@ struct UnitFileInstallInfo {
char **also;
char *default_instance;
+ char *symlink_target;
UnitFileType type;
-
- char *symlink_target;
+ bool auxiliary;
};
static inline bool UNIT_FILE_INSTALL_INFO_HAS_RULES(UnitFileInstallInfo *i) {
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 7ed60dbe87..129706d15f 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -362,22 +362,24 @@ static int compare_unit_info(const void *a, const void *b) {
return strcasecmp(u->id, v->id);
}
+static const char* unit_type_suffix(const char *name) {
+ const char *dot;
+
+ dot = strrchr(name, '.');
+ if (!dot)
+ return "";
+
+ return dot + 1;
+}
+
static bool output_show_unit(const UnitInfo *u, char **patterns) {
assert(u);
if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE))
return false;
- if (arg_types) {
- const char *dot;
-
- dot = strrchr(u->id, '.');
- if (!dot)
- return false;
-
- if (!strv_find(arg_types, dot+1))
- return false;
- }
+ if (arg_types && !strv_find(arg_types, unit_type_suffix(u->id)))
+ return false;
if (arg_all)
return true;
@@ -403,10 +405,10 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
}
static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
- unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len;
+ unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len;
const UnitInfo *u;
unsigned n_shown = 0;
- int job_count = 0;
+ int job_count = 0, desc_len;
max_id_len = strlen("UNIT");
load_len = strlen("LOAD");
@@ -464,18 +466,20 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
for (u = unit_infos; u < unit_infos + c; u++) {
_cleanup_free_ char *e = NULL, *j = NULL;
+ const char *on_underline = "", *off_underline = "";
const char *on_loaded = "", *off_loaded = "";
const char *on_active = "", *off_active = "";
const char *on_circle = "", *off_circle = "";
const char *id;
- bool circle = false;
+ bool circle = false, underline = false;
if (!n_shown && !arg_no_legend) {
if (circle_len > 0)
fputs(" ", stdout);
- printf("%-*s %-*s %-*s %-*s ",
+ printf("%s%-*s %-*s %-*s %-*s ",
+ ansi_underline(),
id_len, "UNIT",
load_len, "LOAD",
active_len, "ACTIVE",
@@ -484,23 +488,33 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
if (job_count)
printf("%-*s ", job_len, "JOB");
- if (!arg_full && arg_no_pager)
- printf("%.*s\n", desc_len, "DESCRIPTION");
- else
- printf("%s\n", "DESCRIPTION");
+ printf("%.*s%s\n",
+ !arg_full && arg_no_pager ? desc_len : -1,
+ "DESCRIPTION",
+ ansi_normal());
}
n_shown++;
+ if (u + 1 < unit_infos + c &&
+ !streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id))) {
+ on_underline = ansi_underline();
+ off_underline = ansi_normal();
+ underline = true;
+ }
+
if (STR_IN_SET(u->load_state, "error", "not-found", "masked") && !arg_plain) {
- on_loaded = ansi_highlight_red();
on_circle = ansi_highlight_yellow();
- off_loaded = off_circle = ansi_normal();
+ off_circle = ansi_normal();
circle = true;
+ on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
+ off_loaded = on_underline;
} else if (streq(u->active_state, "failed") && !arg_plain) {
- on_circle = on_active = ansi_highlight_red();
- off_circle = off_active = ansi_normal();
+ on_circle = ansi_highlight_red();
+ off_circle = ansi_normal();
circle = true;
+ on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
+ off_active = on_underline;
}
if (u->machine) {
@@ -523,17 +537,18 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
if (circle_len > 0)
printf("%s%s%s ", on_circle, circle ? special_glyph(BLACK_CIRCLE) : " ", off_circle);
- printf("%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s",
+ printf("%s%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s",
+ on_underline,
on_active, id_len, id, off_active,
on_loaded, load_len, u->load_state, off_loaded,
on_active, active_len, u->active_state,
sub_len, u->sub_state, off_active,
job_count ? job_len + 1 : 0, u->job_id ? u->job_type : "");
- if (desc_len > 0)
- printf("%.*s\n", desc_len, u->description);
- else
- printf("%s\n", u->description);
+ printf("%.*s%s\n",
+ desc_len > 0 ? desc_len : -1,
+ u->description,
+ off_underline);
}
if (!arg_no_legend) {
@@ -1395,35 +1410,46 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
id_cols = max_id_len;
if (!arg_no_legend && c > 0)
- printf("%-*s %-*s\n",
+ printf("%s%-*s %-*s%s\n",
+ ansi_underline(),
id_cols, "UNIT FILE",
- state_cols, "STATE");
+ state_cols, "STATE",
+ ansi_normal());
for (u = units; u < units + c; u++) {
_cleanup_free_ char *e = NULL;
- const char *on, *off;
+ const char *on, *off, *on_underline = "", *off_underline = "";
const char *id;
+ bool underline = false;
+
+ if (u + 1 < units + c &&
+ !streq(unit_type_suffix(u->path), unit_type_suffix((u + 1)->path))) {
+ on_underline = ansi_underline();
+ off_underline = ansi_normal();
+ underline = true;
+ }
if (IN_SET(u->state,
UNIT_FILE_MASKED,
UNIT_FILE_MASKED_RUNTIME,
UNIT_FILE_DISABLED,
- UNIT_FILE_BAD)) {
- on = ansi_highlight_red();
- off = ansi_normal();
- } else if (u->state == UNIT_FILE_ENABLED) {
- on = ansi_highlight_green();
- off = ansi_normal();
- } else
- on = off = "";
+ UNIT_FILE_BAD))
+ on = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
+ else if (u->state == UNIT_FILE_ENABLED)
+ on = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
+ else
+ on = on_underline;
+ off = off_underline;
id = basename(u->path);
e = arg_full ? NULL : ellipsize(id, id_cols, 33);
- printf("%-*s %s%-*s%s\n",
+ printf("%s%-*s %s%-*s%s%s\n",
+ on_underline,
id_cols, e ? e : id,
- on, state_cols, unit_file_state_to_string(u->state), off);
+ on, state_cols, unit_file_state_to_string(u->state), off,
+ off_underline);
}
if (!arg_no_legend)
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index db1c928660..1686054d2a 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -326,7 +326,9 @@ static void test_default(const char *root) {
assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT);
assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "idontexist.target", false, &changes, &n_changes) == -ENOENT);
- assert_se(n_changes == 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == -ENOENT);
+ assert_se(streq_ptr(changes[0].path, "idontexist.target"));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;