'sd_journal_seek_realtime_usec',
'sd_journal_seek_tail'],
''],
- ['sd_journal_stream_fd', '3', [], ''],
+ ['sd_journal_stream_fd', '3', ['sd_journal_stream_fd_with_namespace'], ''],
['sd_listen_fds',
'3',
['SD_LISTEN_FDS_START', 'sd_listen_fds_with_names'],
<refnamediv>
<refname>sd_journal_stream_fd</refname>
+ <refname>sd_journal_stream_fd_with_namespace</refname>
<refpurpose>Create log stream file descriptor to the journal</refpurpose>
</refnamediv>
<paramdef>int <parameter>level_prefix</parameter></paramdef>
</funcprototype>
+ <funcprototype>
+ <funcdef>int <function>sd_journal_stream_fd_with_namespace</function></funcdef>
+ <paramdef>const char *<parameter>name_space</parameter></paramdef>
+ <paramdef>const char *<parameter>identifier</parameter></paramdef>
+ <paramdef>int <parameter>priority</parameter></paramdef>
+ <paramdef>int <parameter>level_prefix</parameter></paramdef>
+ </funcprototype>
+
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><function>sd_journal_stream_fd()</function> may be used to
- create a log stream file descriptor. Log messages written to this
- file descriptor as simple newline-separated text strings are
- written to the journal. This file descriptor can be used
- internally by applications or be made standard output or standard
- error of other processes executed.</para>
+ <para><function>sd_journal_stream_fd()</function> may be used to create a log stream file descriptor.
+ Log messages written to this file descriptor as simple newline-separated text strings are written
+ to the journal. This file descriptor can be used internally by applications or be made standard output
+ or standard error of other processes executed.</para>
- <para><function>sd_journal_stream_fd()</function> takes a short
- program identifier string as first argument, which will be written
- to the journal as SYSLOG_IDENTIFIER= field for each log entry
+ <para><function>sd_journal_stream_fd()</function> takes a short program identifier string as
+ first argument, which will be written to the journal as SYSLOG_IDENTIFIER= field for each log entry
(see
<citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- for more information). The second argument shall be the default
- priority level for all messages. The priority level is one of
+ for more information). The second argument shall be the default priority level for all messages.
+ The priority level is one of
<constant>LOG_EMERG</constant>, <constant>LOG_ALERT</constant>,
<constant>LOG_CRIT</constant>, <constant>LOG_ERR</constant>,
<constant>LOG_WARNING</constant>, <constant>LOG_NOTICE</constant>,
<citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>
for more information.</para>
- <para>It is recommended that applications log UTF-8 messages only
- with this API, but this is not enforced.</para>
+ <para><function>sd_journal_stream_fd_with_namespace()</function> is similar to
+ <function>sd_journal_stream_fd()</function>, but takes an additional <parameter>name_space</parameter> parameter
+ that specifies which journal namespace to operate on. If specified as <constant>NULL</constant> the call
+ is identical to <function>sd_journal_stream_fd()</function>. For details about journal namespaces, see
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+ <para>It is recommended that applications log UTF-8 messages only with this API, but this is not enforced.</para>
- <para>Each invocation of <function>sd_journal_stream_fd()</function> allocates a new log stream file descriptor,
- that is not shared with prior or later invocations. The file descriptor is write-only (its reading direction is
- shut down), and <constant>O_NONBLOCK</constant> is turned off initially.</para>
+ <para>Each invocation of these functions allocates a new log stream file descriptor,
+ that is not shared with prior or later invocations. The file descriptor is write-only (its reading direction
+ is shut down), and <constant>O_NONBLOCK</constant> is turned off initially.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
- <para>The call returns a valid write-only file descriptor on
- success or a negative errno-style error code.</para>
+ <para>The call returns a valid write-only file descriptor on success or a negative errno-style error code.</para>
</refsect1>
<refsect1>
<title>Signal safety</title>
- <para><function>sd_journal_stream_fd()</function> is "async signal safe" in the meaning of <citerefentry
+ <para><function>sd_journal_stream_fd()</function> and <function>sd_journal_stream_fd_with_namespace()</function>
+ are "async signal safe" in the meaning of <citerefentry
project='man-pages'><refentrytitle>signal-safety</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
</refsect1>
<refsect1>
<title>History</title>
<para><function>sd_journal_stream_fd()</function> was added in version 187.</para>
+ <para><function>sd_journal_stream_fd_with_namespace()</function> was added in version 256.</para>
</refsect1>
<refsect1>
boolean argument.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--namespace=</option></term>
+
+ <listitem><para>Specifies the journal namespace to which the standard IO should be connected.
+ For details about journal namespaces, see
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
<refsect1>
<title>Exit status</title>
- <para>On success, 0 is returned, a non-zero failure code
- otherwise.</para>
+ <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
</refsect1>
<refsect1>
#define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
#define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX)
+static inline char* skip_leading_slash(const char *p) {
+ return skip_leading_chars(p, "/");
+}
+
typedef enum PathSimplifyFlags {
PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0,
} PathSimplifyFlags;
return 0;
}
-static const char *skip_slash(const char *path) {
- assert(path);
- assert(path[0] == '/');
-
- return path + 1;
-}
-
static int verb_status(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
uint64_t left, done;
return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
}
- if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) {
+ if (faccessat(fd, skip_leading_slash(path), F_OK, 0) >= 0) {
puts("indeterminate");
return 0;
}
if (errno != ENOENT)
return log_error_errno(errno, "Failed to check if '%s' exists: %m", path);
- if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) {
+ if (faccessat(fd, skip_leading_slash(good), F_OK, 0) >= 0) {
puts("good");
return 0;
}
if (errno != ENOENT)
return log_error_errno(errno, "Failed to check if '%s' exists: %m", good);
- if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) {
+ if (faccessat(fd, skip_leading_slash(bad), F_OK, 0) >= 0) {
puts("bad");
return 0;
}
if (fd < 0)
return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
- r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target));
+ r = rename_noreplace(fd, skip_leading_slash(source1), fd, skip_leading_slash(target));
if (r == -EEXIST)
goto exists;
if (r == -ENOENT) {
- r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target));
+ r = rename_noreplace(fd, skip_leading_slash(source2), fd, skip_leading_slash(target));
if (r == -EEXIST)
goto exists;
if (r == -ENOENT) {
- if (faccessat(fd, skip_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
+ if (faccessat(fd, skip_leading_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
goto exists;
if (errno != ENOENT)
log_debug("Successfully renamed '%s' to '%s'.", source1, target);
/* First, fsync() the directory these files are located in */
- r = fsync_parent_at(fd, skip_slash(target));
+ r = fsync_parent_at(fd, skip_leading_slash(target));
if (r < 0)
log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
#include "hexdecoct.h"
#include "io-util.h"
#include "iovec-util.h"
+#include "journal-send.h"
#include "missing_ioprio.h"
#include "missing_prctl.h"
#include "missing_securebits.h"
const char *j;
int r;
- j = log_namespace ?
- strjoina("/run/systemd/journal.", log_namespace, "/stdout") :
- "/run/systemd/journal/stdout";
+ assert(fd >= 0);
+
+ j = journal_stream_path(log_namespace);
+ if (!j)
+ return -EINVAL;
if (gid_is_valid(gid)) {
oldgid = getgid();
#include "terminal-util.h"
static const char *arg_identifier = NULL;
+static const char *arg_namespace = NULL;
static int arg_priority = LOG_INFO;
static int arg_stderr_priority = -1;
static bool arg_level_prefix = true;
" -p --priority=PRIORITY Set priority value (0..7)\n"
" --stderr-priority=PRIORITY Set priority value (0..7) used for stderr\n"
" --level-prefix=BOOL Control whether level prefix shall be parsed\n"
+ " --namespace=NAMESPACE Connect to specified journal namespace\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
enum {
ARG_VERSION = 0x100,
ARG_STDERR_PRIORITY,
- ARG_LEVEL_PREFIX
+ ARG_LEVEL_PREFIX,
+ ARG_NAMESPACE,
};
static const struct option options[] = {
{ "priority", required_argument, NULL, 'p' },
{ "stderr-priority", required_argument, NULL, ARG_STDERR_PRIORITY },
{ "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX },
+ { "namespace", required_argument, NULL, ARG_NAMESPACE },
{}
};
return r;
break;
+ case ARG_NAMESPACE:
+ if (isempty(optarg))
+ arg_namespace = NULL;
+ else
+ arg_namespace = optarg;
+ break;
+
case '?':
return -EINVAL;
if (r <= 0)
return r;
- outfd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix);
+ outfd = sd_journal_stream_fd_with_namespace(arg_namespace, arg_identifier, arg_priority, arg_level_prefix);
if (outfd < 0)
return log_error_errno(outfd, "Failed to create stream fd: %m");
if (arg_stderr_priority >= 0 && arg_stderr_priority != arg_priority) {
- errfd = sd_journal_stream_fd(arg_identifier, arg_stderr_priority, arg_level_prefix);
+ errfd = sd_journal_stream_fd_with_namespace(arg_namespace, arg_identifier, arg_stderr_priority, arg_level_prefix);
if (errfd < 0)
return log_error_errno(errfd, "Failed to create stream fd: %m");
}
global:
sd_bus_creds_get_pidfd_dup;
sd_bus_creds_new_from_pidfd;
+ sd_journal_stream_fd_with_namespace;
} LIBSYSTEMD_255;
return fill_iovec_perror_and_send(message, 0, iovec);
}
-_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
+_public_ int sd_journal_stream_fd_with_namespace(
+ const char *name_space,
+ const char *identifier,
+ int priority,
+ int level_prefix) {
+
_cleanup_close_ int fd = -EBADF;
- char *header;
- size_t l;
+ const char *path;
int r;
assert_return(priority >= 0, -EINVAL);
assert_return(priority <= 7, -EINVAL);
+ path = journal_stream_path(name_space);
+ if (!path)
+ return -EINVAL;
+
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
- r = connect_unix_path(fd, AT_FDCWD, "/run/systemd/journal/stdout");
+ r = connect_unix_path(fd, AT_FDCWD, path);
if (r < 0)
return r;
identifier = strempty(identifier);
+ char *header;
+ size_t l;
+
l = strlen(identifier);
header = newa(char, l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
return TAKE_FD(fd);
}
+_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
+ return sd_journal_stream_fd_with_namespace(NULL, identifier, priority, level_prefix);
+}
+
_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
int r;
va_list ap;
#pragma once
#include <stdbool.h>
+#include <stddef.h>
+
+#include "syslog-util.h"
int journal_fd_nonblock(bool nonblock);
void close_journal_fd(void);
+
+/* We declare sd_journal_stream_fd() as async-signal-safe. So instead of strjoin(), which calls malloc()
+ * internally, use a macro + alloca(). */
+#define journal_stream_path(log_namespace) \
+ ({ \
+ const char *_ns = (log_namespace), *_ret; \
+ if (!_ns) \
+ _ret = "/run/systemd/journal/stdout"; \
+ else if (log_namespace_name_valid(_ns)) \
+ _ret = strjoina("/run/systemd/journal.", _ns, "/stdout"); \
+ else \
+ _ret = NULL; \
+ _ret; \
+ })
j->has_persistent_files = true;
}
-static const char *skip_slash(const char *p) {
-
- if (!p)
- return NULL;
-
- while (*p == '/')
- p++;
-
- return p;
-}
-
static int add_any_file(
sd_journal *j,
int fd,
/* If there's a top-level fd defined make the path relative, explicitly, since otherwise
* openat() ignores the first argument. */
- fd = our_fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ fd = our_fd = openat(j->toplevel_fd, skip_leading_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK);
else
fd = our_fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0) {
else
/* Open the specified directory relative to the toplevel fd. Enforce that the path specified is
* relative, by dropping the initial slash */
- d = xopendirat(j->toplevel_fd, skip_slash(path), 0);
+ d = xopendirat(j->toplevel_fd, skip_leading_slash(path), 0);
if (!d)
return -errno;
#endif
int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix);
+int sd_journal_stream_fd_with_namespace(const char *name_space, const char *identifier, int priority, int level_prefix);
/* Browse journal stream */
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemctl enable --now systemd-journald@cat-test.socket
+
+systemd-cat --namespace cat-test env CAT_TEST_RESULT=1
+
+timeout 30 bash -c "until systemctl -q is-active systemd-journald@cat-test.service; do sleep .5; done"
+
+journalctl --namespace cat-test --grep "JOURNAL_STREAM="
+journalctl --namespace cat-test --grep "CAT_TEST_RESULT=1"
+
+systemctl disable --now systemd-journald@cat-test.socket
PassSecurity=yes
ReceiveBuffer=8M
SendBuffer=8M
+
+[Install]
+WantedBy=sockets.target