1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-19 22:50:17 +03:00

Merge 2da37ae9f930836c1a51b0d739bc0fc67ca67f8b into 104587314ff25a5c35390eeb42308f083e1e0488

This commit is contained in:
Andrii Chubatiuk 2025-03-13 22:09:26 +01:00 committed by GitHub
commit eea06798d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 220 additions and 0 deletions

View File

@ -129,6 +129,31 @@
<listitem><para>Takes a boolean value, enforces using compression without content encoding negotiation.
Defaults to <literal>false</literal>.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>Header=</varname></term>
<listitem><para>HTTP header which should be added to each request to URL.</para>
<para>Header consists of name and value, which are separated by colon.</para>
<para>Header name can contains alphanumeric values, "_" and "-" symbols additionally.</para>
<para>This option may be specified more than once, in which case all listed headers will be set. If
the same header name is listed more than once, all its unique values will be concatenated with comma.
Setting <literal>Header</literal> to empty string clears all previous assignments.
</para>
<para>Example:
<programlisting>Header=HeaderName: HeaderValue
Header=HeaderName: NewValue
Header=HeaderName: HeaderValue
</programlisting>
adds <literal>HeaderName</literal> header with <literal>HeaderValue, NewValue</literal> to each HTTP request.
</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
</variablelist>

View File

@ -0,0 +1,98 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "escape.h"
#include "journal-header-util.h"
#include "string-util.h"
#include "strv.h"
/* HTTP header name can contains:
- Alphanumeric characters: a-z, A-Z, and 0-9
- The following special characters: - and _ */
#define VALID_HEADER_NAME_CHARS \
ALPHANUMERICAL "_-"
#define VALID_HEADER_NAME_LENGTH 40
#define VALID_HEADER_VALUE_CHARS \
ALPHANUMERICAL "_ :;.,\\/'\"?!(){}[]@<>=-+*#$&`|~^%"
static bool header_name_is_valid(const char *e) {
if (!e)
return false;
if (strlen(e) == 0 || strlen(e) > VALID_HEADER_NAME_LENGTH)
return false;
return in_charset(e, VALID_HEADER_NAME_CHARS);
}
static bool header_value_is_valid(const char *e) {
if (!e)
return false;
return in_charset(e, VALID_HEADER_VALUE_CHARS);
}
int header_put(OrderedHashmap **headers, const char *name, const char *value) {
assert(headers);
if (!header_value_is_valid(value))
return -EINVAL;
if (!header_name_is_valid(name))
return -EINVAL;
return string_strv_ordered_hashmap_put(headers, name, value);
}
int config_parse_header(
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) {
OrderedHashmap **headers = ASSERT_PTR(data);
char *unescaped, *t;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
if (isempty(rvalue)) {
/* an empty string clears the previous assignments. */
*headers = ordered_hashmap_free(*headers);
return 1;
}
r = cunescape(rvalue, 0, &unescaped);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to unescape headers: %s", rvalue);
return 0;
}
t = strchr(unescaped, ':');
if (!t) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Failed to parse header, name: value separator was not found, ignoring: %s", unescaped);
return 0;
}
*t++ = '\0';
r = header_put(headers, strstrip(unescaped), skip_leading_chars(t, WHITESPACE));
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to update headers: %s", rvalue);
return 0;
}
return 1;
}

View File

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "conf-parser.h"
#include "hashmap.h"
int header_put(OrderedHashmap **headers, const char *name, const char *value);
CONFIG_PARSER_PROTOTYPE(config_parse_header);

View File

@ -15,11 +15,13 @@
#include "constants.h"
#include "daemon-util.h"
#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "glob-util.h"
#include "journal-header-util.h"
#include "journal-upload.h"
#include "journal-util.h"
#include "log.h"
@ -59,6 +61,7 @@ static int arg_follow = -1;
static char *arg_save_state = NULL;
static usec_t arg_network_timeout_usec = USEC_INFINITY;
static OrderedHashmap *arg_compression = NULL;
static OrderedHashmap *arg_headers = NULL;
static bool arg_force_compression = false;
STATIC_DESTRUCTOR_REGISTER(arg_url, freep);
@ -72,6 +75,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_machine, freep);
STATIC_DESTRUCTOR_REGISTER(arg_namespace, freep);
STATIC_DESTRUCTOR_REGISTER(arg_save_state, freep);
STATIC_DESTRUCTOR_REGISTER(arg_compression, ordered_hashmap_freep);
STATIC_DESTRUCTOR_REGISTER(arg_headers, ordered_hashmap_freep);
static void close_fd_input(Uploader *u);
@ -226,6 +230,23 @@ int start_upload(Uploader *u,
h = l;
}
char **values;
const char *name;
ORDERED_HASHMAP_FOREACH_KEY(values, name, arg_headers) {
_cleanup_free_ char *joined = strv_join(values, ", ");
if (!joined)
return log_oom();
_cleanup_free_ char *header = strjoin(name, ": ", joined);
if (!header)
return log_oom();
l = curl_slist_append(h, header);
if (!l)
return log_oom();
h = l;
}
u->header = TAKE_PTR(h);
}
@ -657,6 +678,7 @@ static int parse_config(void) {
{ "Upload", "ServerCertificateFile", config_parse_path_or_ignore, 0, &arg_cert },
{ "Upload", "TrustedCertificateFile", config_parse_path_or_ignore, 0, &arg_trust },
{ "Upload", "NetworkTimeoutSec", config_parse_sec, 0, &arg_network_timeout_usec },
{ "Upload", "Header", config_parse_header, 0, &arg_headers },
{ "Upload", "Compression", config_parse_compression, /* with_level */ true, &arg_compression },
{ "Upload", "ForceCompression", config_parse_bool, 0, &arg_force_compression },
{}

View File

@ -4,6 +4,7 @@ systemd_journal_upload_sources = files(
'journal-compression-util.c',
'journal-upload-journal.c',
'journal-upload.c',
'journal-header-util.c',
)
libsystemd_journal_remote_sources = files(
@ -90,6 +91,12 @@ executables += [
},
]
executables += [
test_template + {
'sources' : files('test-journal-header-util.c', 'journal-header-util.c'),
},
]
in_files = [
['journal-upload.conf',
conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_LIBCURL') == 1 and install_sysconfdir_samples],

View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "hashmap.h"
#include "journal-header-util.h"
#include "tests.h"
TEST(header_put) {
_cleanup_ordered_hashmap_free_ OrderedHashmap *headers = NULL;
ASSERT_OK_POSITIVE(header_put(&headers, "NewName", "Val"));
ASSERT_OK_POSITIVE(header_put(&headers, "Name", "FirstName"));
ASSERT_OK_POSITIVE(header_put(&headers, "Name", "Override"));
ASSERT_OK_ZERO(header_put(&headers, "Name", "FirstName"));
ASSERT_ERROR(header_put(&headers, "InvalidN@me", "test"), EINVAL);
ASSERT_ERROR(header_put(&headers, "Name", NULL), EINVAL);
ASSERT_ERROR(header_put(&headers, NULL, "Value"), EINVAL);
ASSERT_OK_POSITIVE(header_put(&headers, "Name", ""));
ASSERT_ERROR(header_put(&headers, "", "Value"), EINVAL);
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -272,3 +272,41 @@ EOF
rm /run/systemd/journal-upload.conf.d/99-test.conf
rm /run/systemd/journal-remote.conf.d/99-test.conf
done
# Let's test sending data with custom headers
echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
journalctl --sync
cat >/run/systemd/journal-remote.conf.d/99-test.conf <<EOF
[Remote]
SplitMode=host
ServerKeyFile=/run/systemd/remote-pki/server.key
ServerCertificateFile=/run/systemd/remote-pki/server.crt
TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
EOF
cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
[Upload]
URL=https://localhost:19532
Header=TestHeader: TestValue
ServerKeyFile=/run/systemd/remote-pki/client.key
ServerCertificateFile=/run/systemd/remote-pki/client.crt
TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
EOF
systemd-analyze cat-config systemd/journal-remote.conf
systemd-analyze cat-config systemd/journal-upload.conf
systemctl restart systemd-journal-remote.socket
systemctl restart systemd-journal-upload
timeout 15 bash -xec 'until systemctl -q is-active systemd-journal-remote.service; do sleep 1; done'
systemctl status systemd-journal-{remote,upload}
# It may take a bit until the whole journal is transferred
timeout 30 bash -xec "until journalctl --directory=/var/log/journal/remote --identifier='$TEST_TAG' --grep='$TEST_MESSAGE'; do sleep 1; done"
systemctl stop systemd-journal-upload
systemctl stop systemd-journal-remote.{socket,service}
rm -rf /var/log/journal/remote/*
rm /run/systemd/journal-upload.conf.d/99-test.conf
rm /run/systemd/journal-remote.conf.d/99-test.conf