From e46b313a626578c768f8dc98d3dc2f7e3d51bdb7 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 16 Jun 2023 09:56:32 +0200 Subject: [PATCH 1/4] test: clean up the new test case a bit Follow-up to 61cecfa0d8. --- test/TEST-04-JOURNAL/test.sh | 4 ---- test/units/testsuite-04.journal.sh | 16 +++++++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/test/TEST-04-JOURNAL/test.sh b/test/TEST-04-JOURNAL/test.sh index 09516d11f5..507dace514 100755 --- a/test/TEST-04-JOURNAL/test.sh +++ b/test/TEST-04-JOURNAL/test.sh @@ -14,10 +14,6 @@ test_append_files() { cp -av "${TEST_BASE_DIR:?}/test-journals/"* "$workspace/test-journals/" inst_binary unzstd - - ( - command -v logger >/dev/null 2>&1 && inst_binary logger - ) } do_test "$@" diff --git a/test/units/testsuite-04.journal.sh b/test/units/testsuite-04.journal.sh index 58c3661391..5e8a0235c5 100755 --- a/test/units/testsuite-04.journal.sh +++ b/test/units/testsuite-04.journal.sh @@ -52,15 +52,13 @@ grep -q '^PRIORITY=6$' /tmp/output (! grep '^SYSLOG_FACILITY=' /tmp/output) # --truncate shows only first line, skip under asan due to logger -if [ -z "${ASAN_OPTIONS+x}${UBSAN_OPTIONS+x}" ] && command -v logger >/dev/null 2>&1 ;then - ID=$(journalctl --new-id128 | sed -n 2p) - logger -t "$ID" $'HEAD\nTAIL\nTAIL' - journalctl --sync - journalctl -q -b -t "$ID" | grep -q HEAD - journalctl -q -b -t "$ID" | grep -q TAIL - journalctl -q -b -t "$ID" --truncate-newline | grep -q HEAD - journalctl -q -b -t "$ID" --truncate-newline | grep -q -v TAIL -fi +ID="$(systemd-id128 new)" +echo -e 'HEAD\nTAIL\nTAIL' | systemd-cat -t "$ID" +journalctl --sync +journalctl -b -t "$ID" | grep -q HEAD +journalctl -b -t "$ID" | grep -q TAIL +journalctl -b -t "$ID" --truncate-newline | grep -q HEAD +journalctl -b -t "$ID" --truncate-newline | grep -q -v TAIL # '-b all' negates earlier use of -b (-b and -m are otherwise exclusive) journalctl -b -1 -b all -m >/dev/null From b2f03d73d931a92baba3a0f2ec316dc415752b4a Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 16 Jun 2023 17:03:27 +0200 Subject: [PATCH 2/4] journal-gatewayd: make MHD_OPTION_EXTERNAL_LOGGER the first option To get rid of the warning on gatewayd startup: microhttpd: MHD_OPTION_EXTERNAL_LOGGER is not the first option specified for the daemon. Some messages may be printed by the standard MHD logger. --- src/journal-remote/journal-gatewayd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 4b4eec0185..f9bff4fb97 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -1005,10 +1005,10 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char *argv[]) { _cleanup_(MHD_stop_daemonp) struct MHD_Daemon *d = NULL; struct MHD_OptionItem opts[] = { - { MHD_OPTION_NOTIFY_COMPLETED, - (intptr_t) request_meta_free, NULL }, { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger, NULL }, + { MHD_OPTION_NOTIFY_COMPLETED, + (intptr_t) request_meta_free, NULL }, { MHD_OPTION_END, 0, NULL }, { MHD_OPTION_END, 0, NULL }, { MHD_OPTION_END, 0, NULL }, From 1d252ea2ae59250f69187a7a55f90304bbe6f35e Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 16 Jun 2023 18:50:08 +0200 Subject: [PATCH 3/4] test: add a couple of tests for systemd-journal-gatewayd --- test/TEST-04-JOURNAL/test.sh | 6 +- test/units/testsuite-04.journal-gatewayd.sh | 116 ++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100755 test/units/testsuite-04.journal-gatewayd.sh diff --git a/test/TEST-04-JOURNAL/test.sh b/test/TEST-04-JOURNAL/test.sh index 507dace514..96fc8b7049 100755 --- a/test/TEST-04-JOURNAL/test.sh +++ b/test/TEST-04-JOURNAL/test.sh @@ -13,7 +13,11 @@ test_append_files() { mkdir -p "$workspace/test-journals/" cp -av "${TEST_BASE_DIR:?}/test-journals/"* "$workspace/test-journals/" - inst_binary unzstd + image_install curl unzstd + image_install -o openssl + # Necessary for RH-based systems, otherwise MHD fails with: + # microhttpd: Failed to initialise TLS session. + image_install -o /etc/crypto-policies/back-ends/gnutls.config } do_test "$@" diff --git a/test/units/testsuite-04.journal-gatewayd.sh b/test/units/testsuite-04.journal-gatewayd.sh new file mode 100755 index 0000000000..1efdc3cae0 --- /dev/null +++ b/test/units/testsuite-04.journal-gatewayd.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +# pipefail is disabled intentionally, as `curl | grep -q` is very SIGPIPE happy + +if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then + echo "Built without systemd-journal-gatewayd support, skipping the test" + exit 0 +fi + +TEST_MESSAGE="-= This is a test message $RANDOM =-" +TEST_TAG="$(systemd-id128 new)" + +echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG" +journalctl --sync +TEST_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')" +BOOT_CURSOR="$(journalctl -q -b -n 0 --show-cursor | awk '{ print $3; }')" + +/usr/lib/systemd/systemd-journal-gatewayd --version +/usr/lib/systemd/systemd-journal-gatewayd --help + +# Default configuration (HTTP, socket activated) +systemctl start systemd-journal-gatewayd.socket + +# /browse +# We should get redirected to /browse by default +curl -Lfs http://localhost:19531 | grep -qF "Journal" +curl -Lfs http://localhost:19531/browse | grep -qF "Journal" +(! curl -Lfs http://localhost:19531/foo/bar/baz) +(! curl -Lfs http://localhost:19531/foo/../../../bar/../baz) + +# /entries +# Accept: text/plain should be the default +curl -Lfs http://localhost:19531/entries | \ + grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" +curl -Lfs --header "Accept: text/plain" http://localhost:19531/entries | \ + grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" +curl -Lfs --header "Accept: application/json" http://localhost:19531/entries | \ + jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" +# FIXME: drop the condition once https://github.com/systemd/systemd/issues/28059 is resolved +if ! systemd-detect-virt -cq; then + curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?boot | \ + jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" +fi +curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ + jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" +# Show 10 entries starting from $BOOT_CURSOR, skip the first 5 +curl -Lfs --header "Accept: application/json" --header "Range: entries=$BOOT_CURSOR:5:10" http://localhost:19531/entries | \ + jq -se "length == 10" +# Check if the specified cursor refers to an existing entry and return just that entry +curl -Lfs --header "Accept: application/json" --header "Range: entries=$TEST_CURSOR" http://localhost:19531/entries?discrete | \ + jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" +# No idea how to properly parse this (jq won't cut it), so let's at least do some sanity checks that every +# line is either empty or begins with data: +curl -Lfs --header "Accept: text/event-stream" http://localhost:19531/entries | \ + awk '!/^(data: \{.+\}|)$/ { exit 1; }' +# Same thing as journalctl --output=export +mkdir /tmp/remote-journal +curl -Lfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries | \ + /usr/lib/systemd/systemd-journal-remote -o /tmp/remote-journal/system.journal --split-mode=none - +journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE" +rm -rf /tmp/remote-journal + +# /machine +curl -Lfs http://localhost:19531/machine | jq + +# /fields +curl -Lfs http://localhost:19531/fields/MESSAGE | grep -qE -- "$TEST_MESSAGE" +curl -Lfs http://localhost:19531/fields/_TRANSPORT +(! curl -Lfs http://localhost:19531/fields) +(! curl -Lfs http://localhost:19531/fields/foo-bar-baz) + +systemctl stop systemd-journal-gatewayd.{socket,service} + +if ! command -v openssl >/dev/null; then + echo "openssl command not available, skipping the HTTPS tests" + exit 0 +fi + +# Generate a self-signed certificate for systemd-journal-gatewayd +# +# Note: older OpenSSL requires a config file with some extra options, unfortunately +cat >/tmp/openssl.conf <Journal" +curl -Lfsk https://localhost:19531/entries | \ + grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" +curl -Lfsk --header "Accept: application/json" https://localhost:19531/entries | \ + jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" +curl -Lfsk https://localhost:19531/machine | jq +curl -Lfsk https://localhost:19531/fields/_TRANSPORT + +kill "$GATEWAYD_PID" From 9ccd59f751bb639d31155dc6c8d3b76c1a98ef1c Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 16 Jun 2023 19:05:57 +0200 Subject: [PATCH 4/4] socket-activate: make a copy of the command name and arguments When we call safe_fork() with the first argument set (process name), we call rename_process() that zeroes out saved argv (that was saved by save_argc_argv() in the main func defined by DEFINE_MAIN_FUNC()). In this case this means that with --accept both the target executable name and its arguments will be empty strings: ``` $ systemd-socket-activate --accept --listen 1111 cat & Listening on [::]:1111 as 3. $ curl localhost:1111 Communication attempt on fd 3. Connection from 127.0.0.1:52948 to [::ffff:127.0.0.1]:1111 Spawned cat (cat) as PID 10576. Execing () Failed to execp (): No such file or directory Child 10576 died with code 1 curl: (56) Recv failure: Connection reset by peer ``` Let's make a copy of the necessary arguments beforehand and use it instead to fix this. --- src/socket-activate/socket-activate.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/socket-activate/socket-activate.c b/src/socket-activate/socket-activate.c index 1caa30d7d4..2e19d125c3 100644 --- a/src/socket-activate/socket-activate.c +++ b/src/socket-activate/socket-activate.c @@ -443,6 +443,7 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char **argv) { _cleanup_close_ int epoll_fd = -EBADF; + _cleanup_strv_free_ char **exec_argv = NULL; int r, n; log_show_color(true); @@ -453,6 +454,12 @@ static int run(int argc, char **argv) { if (r <= 0) return r; + exec_argv = strv_copy(arg_args); + if (!exec_argv) + return log_oom(); + + assert(strv_length(exec_argv) > 0); + r = install_chld_handler(); if (r < 0) return r; @@ -475,14 +482,14 @@ static int run(int argc, char **argv) { log_info("Communication attempt on fd %i.", event.data.fd); if (arg_accept) { - r = do_accept(argv[optind], argv + optind, event.data.fd); + r = do_accept(exec_argv[0], exec_argv, event.data.fd); if (r < 0) return r; } else break; } - return exec_process(argv[optind], argv + optind, SD_LISTEN_FDS_START, (size_t) n); + return exec_process(exec_argv[0], exec_argv, SD_LISTEN_FDS_START, (size_t) n); } DEFINE_MAIN_FUNCTION(run);