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