803fb7
From 32b4631e74262e02094c6402d39d1e4264442037 Mon Sep 17 00:00:00 2001
803fb7
From: Lennart Poettering <lennart@poettering.net>
803fb7
Date: Thu, 9 Apr 2015 18:32:21 +0200
803fb7
Subject: [PATCH] util: add shell_maybe_quote() call for preparing a string for
803fb7
 shell cmdline inclusion
803fb7
803fb7
If necessary the passed string is enclosed in "", and all special
803fb7
characters escapes.
803fb7
803fb7
This also ports over usage in bus-util.c and job.c to use this, instead
803fb7
of a incorrect local implementation that forgets to properly escape.
803fb7
803fb7
Conflicts:
803fb7
	src/shared/util.c
803fb7
	src/shared/util.h
803fb7
803fb7
Cherry-picked from: 019c7fba
803fb7
Resolves: #1016680
803fb7
---
de8967
 src/core/job.c                   |  8 +++---
de8967
 src/libsystemd/sd-bus/bus-util.c | 15 +++++------
de8967
 src/shared/util.c                | 45 ++++++++++++++++++++++++++++++--
803fb7
 src/shared/util.h                |  2 ++
de8967
 src/test/test-util.c             | 19 ++++++++++++++
803fb7
 5 files changed, 74 insertions(+), 15 deletions(-)
803fb7
803fb7
diff --git a/src/core/job.c b/src/core/job.c
803fb7
index 4740ff18c..7416386a1 100644
803fb7
--- a/src/core/job.c
803fb7
+++ b/src/core/job.c
803fb7
@@ -679,15 +679,13 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
803fb7
                         break;
803fb7
 
803fb7
                 case JOB_FAILED: {
803fb7
-                        bool quotes;
803fb7
+                        _cleanup_free_ char *quoted = NULL;
803fb7
 
803fb7
-                        quotes = chars_intersect(u->id, SHELL_NEED_QUOTES);
803fb7
+                        quoted = shell_maybe_quote(u->id);
803fb7
 
803fb7
                         manager_flip_auto_status(u->manager, true);
803fb7
                         unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format);
803fb7
-                        manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL,
803fb7
-                                              "See \"systemctl status %s%s%s\" for details.",
803fb7
-                                              quotes ? "'" : "", u->id, quotes ? "'" : "");
803fb7
+                        manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
803fb7
                         break;
803fb7
                 }
803fb7
 
803fb7
diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c
803fb7
index 52d4ebe61..b0a5a7592 100644
803fb7
--- a/src/libsystemd/sd-bus/bus-util.c
803fb7
+++ b/src/libsystemd/sd-bus/bus-util.c
803fb7
@@ -1709,16 +1709,15 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) {
803fb7
                 else if (streq(d->result, "unsupported"))
803fb7
                         log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
803fb7
                 else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
803fb7
-                        if (d->name) {
803fb7
-                                bool quotes;
803fb7
+                        _cleanup_free_ char *quoted = NULL;
803fb7
 
803fb7
-                                quotes = chars_intersect(d->name, SHELL_NEED_QUOTES);
803fb7
+                        if (d->name)
803fb7
+                                quoted = shell_maybe_quote(d->name);
803fb7
 
803fb7
-                                log_error("Job for %s failed. See \"systemctl status %s%s%s\" and \"journalctl -xe\" for details.",
803fb7
-                                          d->name,
803fb7
-                                          quotes ? "'" : "", d->name, quotes ? "'" : "");
803fb7
-                        } else
803fb7
-                                log_error("Job failed. See \"journalctl -xe\" for details.");
803fb7
+                        if (quoted)
803fb7
+                                log_error("Job for %s failed. See 'systemctl status %s' and 'journalctl -xe' for details.", d->name, quoted);
803fb7
+                        else
803fb7
+                                log_error("Job failed. See 'journalctl -xe' for details.");
803fb7
                 }
803fb7
         }
803fb7
 
803fb7
diff --git a/src/shared/util.c b/src/shared/util.c
803fb7
index 649344d88..778c2b0e0 100644
803fb7
--- a/src/shared/util.c
803fb7
+++ b/src/shared/util.c
803fb7
@@ -1329,7 +1329,8 @@ char *cescape(const char *s) {
803fb7
 
803fb7
         assert(s);
803fb7
 
803fb7
-        /* Does C style string escaping. */
803fb7
+        /* Does C style string escaping. May be be reversed with
803fb7
+         * cunescape(). */
803fb7
 
803fb7
         r = new(char, strlen(s)*4 + 1);
803fb7
         if (!r)
803fb7
@@ -1493,7 +1494,7 @@ char *xescape(const char *s, const char *bad) {
803fb7
 
803fb7
         /* Escapes all chars in bad, in addition to \ and all special
803fb7
          * chars, in \xFF style escaping. May be reversed with
803fb7
-         * cunescape. */
803fb7
+         * cunescape(). */
803fb7
 
803fb7
         r = new(char, strlen(s) * 4 + 1);
803fb7
         if (!r)
803fb7
@@ -8101,3 +8102,43 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
803fb7
 
803fb7
         return -1;
803fb7
 }
803fb7
+
803fb7
+char *shell_maybe_quote(const char *s) {
803fb7
+        const char *p;
803fb7
+        char *r, *t;
803fb7
+
803fb7
+        assert(s);
803fb7
+
803fb7
+        /* Encloses a string in double quotes if necessary to make it
803fb7
+         * OK as shell string. */
803fb7
+
803fb7
+        for (p = s; *p; p++)
803fb7
+                if (*p <= ' ' ||
803fb7
+                    *p >= 127 ||
803fb7
+                    strchr(SHELL_NEED_QUOTES, *p))
803fb7
+                        break;
803fb7
+
803fb7
+        if (!*p)
803fb7
+                return strdup(s);
803fb7
+
803fb7
+        r = new(char, 1+strlen(s)*2+1+1);
803fb7
+        if (!r)
803fb7
+                return NULL;
803fb7
+
803fb7
+        t = r;
803fb7
+        *(t++) = '"';
803fb7
+        t = mempcpy(t, s, p - s);
803fb7
+
803fb7
+        for (; *p; p++) {
803fb7
+
803fb7
+                if (strchr(SHELL_NEED_ESCAPE, *p))
803fb7
+                        *(t++) = '\\';
803fb7
+
803fb7
+                *(t++) = *p;
803fb7
+        }
803fb7
+
803fb7
+        *(t++)= '"';
803fb7
+        *t = 0;
803fb7
+
803fb7
+        return r;
803fb7
+}
803fb7
diff --git a/src/shared/util.h b/src/shared/util.h
803fb7
index a83b58822..7ecfd8571 100644
803fb7
--- a/src/shared/util.h
803fb7
+++ b/src/shared/util.h
803fb7
@@ -1078,3 +1078,5 @@ void sigkill_wait(pid_t *pid);
803fb7
 #define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait)
803fb7
 
803fb7
 int syslog_parse_priority(const char **p, int *priority, bool with_facility);
803fb7
+
803fb7
+char *shell_maybe_quote(const char *s);
803fb7
diff --git a/src/test/test-util.c b/src/test/test-util.c
803fb7
index 9515a8cbf..9ae347b43 100644
803fb7
--- a/src/test/test-util.c
803fb7
+++ b/src/test/test-util.c
803fb7
@@ -1512,6 +1512,24 @@ static void test_sparse_write(void) {
803fb7
         test_sparse_write_one(fd, test_e, sizeof(test_e));
803fb7
 }
803fb7
 
803fb7
+static void test_shell_maybe_quote_one(const char *s, const char *expected) {
803fb7
+        _cleanup_free_ char *r;
803fb7
+
803fb7
+        assert_se(r = shell_maybe_quote(s));
803fb7
+        assert_se(streq(r, expected));
803fb7
+}
803fb7
+
803fb7
+static void test_shell_maybe_quote(void) {
803fb7
+
803fb7
+        test_shell_maybe_quote_one("", "");
803fb7
+        test_shell_maybe_quote_one("\\", "\"\\\\\"");
803fb7
+        test_shell_maybe_quote_one("\"", "\"\\\"\"");
803fb7
+        test_shell_maybe_quote_one("foobar", "foobar");
803fb7
+        test_shell_maybe_quote_one("foo bar", "\"foo bar\"");
803fb7
+        test_shell_maybe_quote_one("foo \"bar\" waldo", "\"foo \\\"bar\\\" waldo\"");
803fb7
+        test_shell_maybe_quote_one("foo$bar", "\"foo\\$bar\"");
803fb7
+}
803fb7
+
803fb7
 int main(int argc, char *argv[]) {
803fb7
         log_parse_environment();
803fb7
         log_open();
803fb7
@@ -1589,6 +1607,7 @@ int main(int argc, char *argv[]) {
803fb7
         test_same_fd();
803fb7
         test_uid_ptr();
803fb7
         test_sparse_write();
803fb7
+        test_shell_maybe_quote();
803fb7
 
803fb7
         return 0;
803fb7
 }