summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--man/systemd.exec.xml20
-rw-r--r--src/basic/formats-util.h16
-rw-r--r--src/core/execute.c72
4 files changed, 95 insertions, 14 deletions
diff --git a/configure.ac b/configure.ac
index ffc6eedcdd..7fd78bfb60 100644
--- a/configure.ac
+++ b/configure.ac
@@ -249,6 +249,7 @@ AC_CHECK_SIZEOF(uid_t)
AC_CHECK_SIZEOF(gid_t)
AC_CHECK_SIZEOF(time_t)
AC_CHECK_SIZEOF(dev_t)
+AC_CHECK_SIZEOF(ino_t)
AC_CHECK_SIZEOF(rlim_t,,[
#include <sys/time.h>
#include <sys/resource.h>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index a39e800854..dbfc7692f7 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1539,6 +1539,26 @@
<citerefentry project='man-pages'><refentrytitle>termcap</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>$JOURNAL_STREAM</varname></term>
+
+ <listitem><para>If the standard output or standard error output of the executed processes are connected to the
+ journal (for example, by setting <varname>StandardError=journal</varname>) <varname>$JOURNAL_STREAM</varname>
+ contains the device and inode numbers of the connection file descriptor, formatted in decimal, separated by a
+ colon (<literal>:</literal>). This permits invoked processes to safely detect whether their standard output or
+ standard error output are connected to the journal. The device and inode numbers of the file descriptors should
+ be compared with the values set in the environment variable to determine whether the process output is still
+ connected to the journal. Note that it is generally not sufficient to only check whether
+ <varname>$JOURNAL_STREAM</varname> is set at all as services might invoke external processes replacing their
+ standard output or standard error output, without unsetting the environment variable.</para>
+
+ <para>This environment variable is primarily useful to allow services to optionally upgrade their used log
+ protocol to the native journal protocol (using
+ <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry> and other
+ functions) if their standard output or standard error output is connected to the journal anyway, thus enabling
+ delivery of structured metadata along with logged messages.</para></listitem>
+ </varlistentry>
</variablelist>
<para>Additional variables may be configured by the following
diff --git a/src/basic/formats-util.h b/src/basic/formats-util.h
index 9b4e8e98fa..39a185f59b 100644
--- a/src/basic/formats-util.h
+++ b/src/basic/formats-util.h
@@ -61,3 +61,19 @@
#else
# error Unknown rlim_t size
#endif
+
+#if SIZEOF_DEV_T == 8
+# define DEV_FMT "%" PRIu64
+#elif SIZEOF_DEV_T == 4
+# define DEV_FMT "%" PRIu32
+#else
+# error Unknown dev_t size
+#endif
+
+#if SIZEOF_INO_T == 8
+# define INO_FMT "%" PRIu64
+#elif SIZEOF_INO_T == 4
+# define INO_FMT "%" PRIu32
+#else
+# error Unknown ino_t size
+#endif
diff --git a/src/core/execute.c b/src/core/execute.c
index c20650626c..3c3369373f 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -289,7 +289,15 @@ static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
return r;
}
-static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd, uid_t uid, gid_t gid) {
+static int connect_logger_as(
+ const ExecContext *context,
+ ExecOutput output,
+ const char *ident,
+ const char *unit_id,
+ int nfd,
+ uid_t uid,
+ gid_t gid) {
+
int fd, r;
assert(context);
@@ -310,7 +318,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
return -errno;
}
- fd_inc_sndbuf(fd, SNDBUF_SIZE);
+ (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
dprintf(fd,
"%s\n"
@@ -328,11 +336,11 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
is_terminal_output(output));
- if (fd != nfd) {
- r = dup2(fd, nfd) < 0 ? -errno : nfd;
- safe_close(fd);
- } else
- r = nfd;
+ if (fd == nfd)
+ return nfd;
+
+ r = dup2(fd, nfd) < 0 ? -errno : nfd;
+ safe_close(fd);
return r;
}
@@ -446,7 +454,10 @@ static int setup_output(
int fileno,
int socket_fd,
const char *ident,
- uid_t uid, gid_t gid) {
+ uid_t uid,
+ gid_t gid,
+ dev_t *journal_stream_dev,
+ ino_t *journal_stream_ino) {
ExecOutput o;
ExecInput i;
@@ -456,6 +467,8 @@ static int setup_output(
assert(context);
assert(params);
assert(ident);
+ assert(journal_stream_dev);
+ assert(journal_stream_ino);
if (fileno == STDOUT_FILENO && params->stdout_fd >= 0) {
@@ -535,6 +548,17 @@ static int setup_output(
if (r < 0) {
log_unit_error_errno(unit, r, "Failed to connect %s to the journal socket, ignoring: %m", fileno == STDOUT_FILENO ? "stdout" : "stderr");
r = open_null_as(O_WRONLY, fileno);
+ } else {
+ struct stat st;
+
+ /* If we connected this fd to the journal via a stream, patch the device/inode into the passed
+ * parameters, but only then. This is useful so that we can set $JOURNAL_STREAM that permits
+ * services to detect whether they are connected to the journal or not. */
+
+ if (fstat(fileno, &st) >= 0) {
+ *journal_stream_dev = st.st_dev;
+ *journal_stream_ino = st.st_ino;
+ }
}
return r;
@@ -1278,6 +1302,8 @@ static int build_environment(
const char *home,
const char *username,
const char *shell,
+ dev_t journal_stream_dev,
+ ino_t journal_stream_ino,
char ***ret) {
_cleanup_strv_free_ char **our_env = NULL;
@@ -1287,7 +1313,7 @@ static int build_environment(
assert(c);
assert(ret);
- our_env = new0(char*, 11);
+ our_env = new0(char*, 12);
if (!our_env)
return -ENOMEM;
@@ -1359,8 +1385,15 @@ static int build_environment(
our_env[n_env++] = x;
}
+ if (journal_stream_dev != 0 && journal_stream_ino != 0) {
+ if (asprintf(&x, "JOURNAL_STREAM=" DEV_FMT ":" INO_FMT, journal_stream_dev, journal_stream_ino) < 0)
+ return -ENOMEM;
+
+ our_env[n_env++] = x;
+ }
+
our_env[n_env++] = NULL;
- assert(n_env <= 11);
+ assert(n_env <= 12);
*ret = our_env;
our_env = NULL;
@@ -1473,10 +1506,12 @@ static int exec_child(
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
_cleanup_free_ char *mac_selinux_context_net = NULL;
const char *username = NULL, *home = NULL, *shell = NULL, *wd;
+ dev_t journal_stream_dev = 0;
+ ino_t journal_stream_ino = 0;
+ bool needs_mount_namespace;
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
int i, r;
- bool needs_mount_namespace;
assert(unit);
assert(command);
@@ -1576,13 +1611,13 @@ static int exec_child(
return r;
}
- r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid);
+ r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, 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);
+ r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDERR;
return r;
@@ -1721,7 +1756,16 @@ static int exec_child(
}
}
- r = build_environment(context, params, n_fds, home, username, shell, &our_env);
+ r = build_environment(
+ context,
+ params,
+ n_fds,
+ home,
+ username,
+ shell,
+ journal_stream_dev,
+ journal_stream_ino,
+ &our_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return r;