From 2b99f645c672efa239a6b8ab1ee2c084474cebba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 6 Apr 2020 09:50:51 +0200 Subject: [PATCH 1/2] shared/escape: add new escape style with \n\t escaped --- src/basic/escape.c | 18 ++++++++++++------ src/basic/escape.h | 9 +++++++-- src/test/test-escape.c | 11 +++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/basic/escape.c b/src/basic/escape.c index c5c44d2e7d8..116efa4119d 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -518,22 +518,28 @@ char* shell_maybe_quote(const char *s, EscapeStyle style) { return NULL; t = r; - if (style == ESCAPE_BACKSLASH) + switch (style) { + case ESCAPE_BACKSLASH: + case ESCAPE_BACKSLASH_ONELINE: *(t++) = '"'; - else if (style == ESCAPE_POSIX) { + break; + case ESCAPE_POSIX: *(t++) = '$'; *(t++) = '\''; - } else + break; + default: assert_not_reached("Bad EscapeStyle"); + } t = mempcpy(t, s, p - s); - if (style == ESCAPE_BACKSLASH) - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false); + if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, + style == ESCAPE_BACKSLASH_ONELINE); else t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); - if (style == ESCAPE_BACKSLASH) + if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) *(t++) = '"'; else *(t++) = '\''; diff --git a/src/basic/escape.h b/src/basic/escape.h index b8eb137c3d3..0b00b116edc 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -34,8 +34,13 @@ typedef enum UnescapeFlags { } UnescapeFlags; typedef enum EscapeStyle { - ESCAPE_BACKSLASH = 1, - ESCAPE_POSIX = 2, + ESCAPE_BACKSLASH = 1, /* Add shell quotes ("") so the shell will consider this a single + argument, possibly multiline. Tabs and newlines are not escaped. */ + ESCAPE_BACKSLASH_ONELINE = 2, /* Similar to ESCAPE_BACKSLASH, but always produces a single-line + string instead. Shell escape sequences are produced for tabs and + newlines. */ + ESCAPE_POSIX = 3, /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape + * syntax (a string enclosed in $'') instead of plain quotes. */ } EscapeStyle; char *cescape(const char *s); diff --git a/src/test/test-escape.c b/src/test/test-escape.c index f6aae1eb182..699747fcc3c 100644 --- a/src/test/test-escape.c +++ b/src/test/test-escape.c @@ -142,31 +142,42 @@ static void test_shell_maybe_quote_one(const char *s, static void test_shell_maybe_quote(void) { test_shell_maybe_quote_one("", ESCAPE_BACKSLASH, ""); + test_shell_maybe_quote_one("", ESCAPE_BACKSLASH_ONELINE, ""); test_shell_maybe_quote_one("", ESCAPE_POSIX, ""); test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH, "\"\\\\\""); + test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH_ONELINE, "\"\\\\\""); test_shell_maybe_quote_one("\\", ESCAPE_POSIX, "$'\\\\'"); test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH, "\"\\\"\""); + test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH_ONELINE, "\"\\\"\""); test_shell_maybe_quote_one("\"", ESCAPE_POSIX, "$'\"'"); test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH, "foobar"); + test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH_ONELINE, "foobar"); test_shell_maybe_quote_one("foobar", ESCAPE_POSIX, "foobar"); test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH, "\"foo bar\""); + test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH_ONELINE, "\"foo bar\""); test_shell_maybe_quote_one("foo bar", ESCAPE_POSIX, "$'foo bar'"); test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH, "\"foo\tbar\""); + test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\tbar\""); test_shell_maybe_quote_one("foo\tbar", ESCAPE_POSIX, "$'foo\\tbar'"); test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH, "\"foo\nbar\""); + test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\nbar\""); test_shell_maybe_quote_one("foo\nbar", ESCAPE_POSIX, "$'foo\\nbar'"); test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH, "\"foo \\\"bar\\\" waldo\""); + test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH_ONELINE, "\"foo \\\"bar\\\" waldo\""); test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_POSIX, "$'foo \"bar\" waldo'"); test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH, "\"foo\\$bar\""); + test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\$bar\""); test_shell_maybe_quote_one("foo$bar", ESCAPE_POSIX, "$'foo$bar'"); /* Note that current users disallow control characters, so this "test" * is here merely to establish current behaviour. If control characters * were allowed, they should be quoted, i.e. \001 should become \\001. */ test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH, "\"a\nb\001\""); + test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH_ONELINE, "\"a\\nb\001\""); test_shell_maybe_quote_one("a\nb\001", ESCAPE_POSIX, "$'a\\nb\001'"); test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH, "\"foo!bar\""); + test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH_ONELINE, "\"foo!bar\""); test_shell_maybe_quote_one("foo!bar", ESCAPE_POSIX, "$'foo!bar'"); } From 241c4b6adae3ec015f69b34174511e6912d1f0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 6 Apr 2020 09:57:07 +0200 Subject: [PATCH 2/2] systemctl: show Environment entries with whitespace This makes the Environment entries more round-trippable: a similar format is used for input and output. It is certainly more useful for users, because showing [unprintable] on anything non-trivial makes systemctl show -p Environment useless in many cases. Fixes: #14723 and https://bugzilla.redhat.com/show_bug.cgi?id=1525593. $ systemctl --user show -p Environment run-*.service Environment=ASDF=asfd "SPACE= " Environment=ASDF=asfd "SPACE=\n\n\n" Environment=ASDF=asfd "TAB=\t\\" "FOO=X X" --- src/shared/bus-util.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 4b0a3a3e317..b4023dfbd4d 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -21,6 +21,7 @@ #include "bus-util.h" #include "cap-list.h" #include "cgroup-util.h" +#include "escape.h" #include "mountpoint-util.h" #include "nsflags.h" #include "parse-util.h" @@ -500,18 +501,20 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b return r; while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) { - bool good; + _cleanup_free_ char *e = NULL; - if (first && !value) - printf("%s=", name); + e = shell_maybe_quote(str, ESCAPE_BACKSLASH_ONELINE); + if (!e) + return -ENOMEM; - /* This property has multiple space-separated values, so - * neither spaces nor newlines can be allowed in a value. */ - good = str[strcspn(str, " \n")] == '\0'; + if (first) { + if (!value) + printf("%s=", name); + first = false; + } else + fputs(" ", stdout); - printf("%s%s", first ? "" : " ", good ? str : "[unprintable]"); - - first = false; + fputs(e, stdout); } if (r < 0) return r;