mirror of
https://github.com/systemd/systemd
synced 2024-10-04 15:21:01 +00:00
Merge pull request #31754 from YHNdnzj/journal-fd-namespace
journal/cat: allow connecting output to specific journal namespace
This commit is contained in:
commit
cdafb51ab4
|
@ -795,7 +795,7 @@ manpages = [
|
|||
'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'],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
<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>
|
||||
|
||||
|
@ -31,26 +32,31 @@
|
|||
<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>,
|
||||
|
@ -63,25 +69,30 @@
|
|||
<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>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>It is recommended that applications log UTF-8 messages only with this API, but this is not enforced.</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>
|
||||
|
@ -106,6 +117,7 @@
|
|||
<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>
|
||||
|
|
|
@ -123,6 +123,17 @@
|
|||
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>
|
||||
|
@ -130,8 +141,7 @@
|
|||
<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>
|
||||
|
|
|
@ -76,6 +76,10 @@ char* path_extend_internal(char **x, ...);
|
|||
#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;
|
||||
|
|
|
@ -316,13 +316,6 @@ static int make_bad(const char *prefix, uint64_t done, const char *suffix, char
|
|||
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;
|
||||
|
@ -370,14 +363,14 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
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;
|
||||
}
|
||||
|
@ -385,7 +378,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
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;
|
||||
}
|
||||
|
@ -445,17 +438,17 @@ static int verb_set(int argc, char *argv[], void *userdata) {
|
|||
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)
|
||||
|
@ -474,7 +467,7 @@ static int verb_set(int argc, char *argv[], void *userdata) {
|
|||
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");
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#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"
|
||||
|
@ -159,9 +160,11 @@ static int connect_journal_socket(
|
|||
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();
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#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;
|
||||
|
@ -44,6 +45,7 @@ static int help(void) {
|
|||
" -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(),
|
||||
|
@ -58,7 +60,8 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_STDERR_PRIORITY,
|
||||
ARG_LEVEL_PREFIX
|
||||
ARG_LEVEL_PREFIX,
|
||||
ARG_NAMESPACE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
|
@ -68,6 +71,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "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 },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -117,6 +121,13 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
return r;
|
||||
break;
|
||||
|
||||
case ARG_NAMESPACE:
|
||||
if (isempty(optarg))
|
||||
arg_namespace = NULL;
|
||||
else
|
||||
arg_namespace = optarg;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -137,12 +148,12 @@ static int run(int argc, char *argv[]) {
|
|||
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");
|
||||
}
|
||||
|
|
|
@ -839,4 +839,5 @@ LIBSYSTEMD_256 {
|
|||
global:
|
||||
sd_bus_creds_get_pidfd_dup;
|
||||
sd_bus_creds_new_from_pidfd;
|
||||
sd_journal_stream_fd_with_namespace;
|
||||
} LIBSYSTEMD_255;
|
||||
|
|
|
@ -398,20 +398,28 @@ _public_ int sd_journal_perror(const char *message) {
|
|||
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;
|
||||
|
||||
|
@ -422,6 +430,9 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve
|
|||
|
||||
identifier = strempty(identifier);
|
||||
|
||||
char *header;
|
||||
size_t l;
|
||||
|
||||
l = strlen(identifier);
|
||||
header = newa(char, l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
|
||||
|
||||
|
@ -446,6 +457,10 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve
|
|||
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;
|
||||
|
|
|
@ -2,6 +2,23 @@
|
|||
#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; \
|
||||
})
|
||||
|
|
|
@ -1473,17 +1473,6 @@ static void track_file_disposition(sd_journal *j, JournalFile *f) {
|
|||
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,
|
||||
|
@ -1503,7 +1492,7 @@ static int add_any_file(
|
|||
/* 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) {
|
||||
|
@ -1811,7 +1800,7 @@ static int directory_open(sd_journal *j, const char *path, DIR **ret) {
|
|||
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;
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ int sd_journal_perror_with_location(const char *file, const char *line, const ch
|
|||
#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 */
|
||||
|
||||
|
|
15
test/units/testsuite-04.cat.sh
Executable file
15
test/units/testsuite-04.cat.sh
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/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
|
|
@ -22,3 +22,6 @@ PassCredentials=yes
|
|||
PassSecurity=yes
|
||||
ReceiveBuffer=8M
|
||||
SendBuffer=8M
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
|
|
Loading…
Reference in a new issue