]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #12590 from keszybz/unicode-cmdlines
authorLennart Poettering <lennart@poettering.net>
Fri, 24 May 2019 08:41:30 +0000 (10:41 +0200)
committerGitHub <noreply@github.com>
Fri, 24 May 2019 08:41:30 +0000 (10:41 +0200)
Use unicode for cmdline printing

21 files changed:
TODO
src/basic/env-util.c
src/basic/env-util.h
src/basic/escape.c
src/basic/escape.h
src/basic/proc-cmdline.c
src/basic/process-util.c
src/basic/process-util.h
src/basic/string-util.h
src/basic/utf8.c
src/basic/utf8.h
src/cgtop/cgtop.c
src/core/dbus-unit.c
src/coredump/coredump.c
src/journal/journald-context.c
src/shared/cgroup-show.c
src/shared/cgroup-show.h
src/test/test-escape.c
src/test/test-process-util.c
src/test/test-utf8.c
test/TEST-13-NSPAWN-SMOKE/test.sh

diff --git a/TODO b/TODO
index d87a70a0b5327a5cd6a78a1e44261103cc712def..e8f59d0932fb325f4b3f996103ebcab43dd64d10 100644 (file)
--- a/TODO
+++ b/TODO
@@ -4,6 +4,11 @@ Bugfixes:
   manager or system manager can be always set. It would be better to reject
   them when parsing config.
 
+* busctl --user call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager GetUnitProcesses "s" run-rbff1b85427b34ba3adf864281aeda8e7.service
+Failed to set address: No such file or directory
+
+  → improve error message
+
 External:
 
 * Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros.
index fd449dcce07d1c0badf7f5b614646b04751c66a1..896eec58356a0673ec7feb1a951e0b0839123bea 100644 (file)
@@ -72,7 +72,7 @@ bool env_value_is_valid(const char *e) {
          * either. Discounting the shortest possible variable name of
          * length 1, the equal sign and trailing NUL this hence leaves
          * ARG_MAX-3 as longest possible variable value. */
-        if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3)
+        if (strlen(e) > sc_arg_max() - 3)
                 return false;
 
         return true;
@@ -95,7 +95,7 @@ bool env_assignment_is_valid(const char *e) {
          * be > ARG_MAX, hence the individual variable assignments
          * cannot be either, but let's leave room for one trailing NUL
          * byte. */
-        if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1)
+        if (strlen(e) > sc_arg_max() - 1)
                 return false;
 
         return true;
index d54f99658bddaebdcaa54437629ba6126e483c13..92802ed774448ef867ef289c54ecc280cfea368e 100644 (file)
@@ -4,10 +4,17 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <unistd.h>
 
 #include "macro.h"
 #include "string.h"
 
+static inline size_t sc_arg_max(void) {
+        long l = sysconf(_SC_ARG_MAX);
+        assert(l > 0);
+        return (size_t) l;
+}
+
 bool env_name_is_valid(const char *e);
 bool env_value_is_valid(const char *e);
 bool env_assignment_is_valid(const char *e);
index 5f715156fbf407e6c03d1a1839147ac7e76dbc96..33a6f204f55f5efb92e673c752cfac3f72a27d91 100644 (file)
@@ -368,33 +368,78 @@ int cunescape(const char *s, UnescapeFlags flags, char **ret) {
         return cunescape_length(s, strlen(s), flags, ret);
 }
 
-char *xescape(const char *s, const char *bad) {
-        char *r, *t;
+char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
+        char *ans, *t, *prev, *prev2;
         const char *f;
 
-        /* Escapes all chars in bad, in addition to \ and all special
-         * chars, in \xFF style escaping. May be reversed with
-         * cunescape(). */
+        /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
+         * reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged.
+         * This corresponds to non-ASCII printable characters in pre-unicode encodings.
+         *
+         * If console_width is reached, output is truncated and "..." is appended. */
 
-        r = new(char, strlen(s) * 4 + 1);
-        if (!r)
+        if (console_width == 0)
+                return strdup("");
+
+        ans = new(char, MIN(strlen(s), console_width) * 4 + 1);
+        if (!ans)
                 return NULL;
 
-        for (f = s, t = r; *f; f++) {
+        memset(ans, '_', MIN(strlen(s), console_width) * 4);
+        ans[MIN(strlen(s), console_width) * 4] = 0;
+
+        for (f = s, t = prev = prev2 = ans; ; f++) {
+                char *tmp_t = t;
+
+                if (!*f) {
+                        *t = 0;
+                        return ans;
+                }
+
+                if ((unsigned char) *f < ' ' || (!eight_bits && (unsigned char) *f >= 127) ||
+                    *f == '\\' || strchr(bad, *f)) {
+                        if ((size_t) (t - ans) + 4 > console_width)
+                                break;
 
-                if ((*f < ' ') || (*f >= 127) ||
-                    (*f == '\\') || strchr(bad, *f)) {
                         *(t++) = '\\';
                         *(t++) = 'x';
                         *(t++) = hexchar(*f >> 4);
                         *(t++) = hexchar(*f);
-                } else
+                } else {
+                        if ((size_t) (t - ans) + 1 > console_width)
+                                break;
+
                         *(t++) = *f;
+                }
+
+                /* We might need to go back two cycles to fit three dots, so remember two positions */
+                prev2 = prev;
+                prev = tmp_t;
         }
 
-        *t = 0;
+        /* We can just write where we want, since chars are one-byte */
+        size_t c = MIN(console_width, 3u); /* If the console is too narrow, write fewer dots */
+        size_t off;
+        if (console_width - c >= (size_t) (t - ans))
+                off = (size_t) (t - ans);
+        else if (console_width - c >= (size_t) (prev - ans))
+                off = (size_t) (prev - ans);
+        else if (console_width - c >= (size_t) (prev2 - ans))
+                off = (size_t) (prev2 - ans);
+        else
+                off = console_width - c;
+        assert(off <= (size_t) (t - ans));
 
-        return r;
+        memcpy(ans + off, "...", c);
+        ans[off + c] = '\0';
+        return ans;
+}
+
+char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) {
+        if (eight_bit)
+                return xescape_full(str, "", console_width, true);
+        else
+                return utf8_escape_non_printable_full(str, console_width);
 }
 
 char *octescape(const char *s, size_t len) {
index 515620993d051b4df03074acf3f5736937053ee0..b26054c5df8abb765e7c68163c3c47ca41906c22 100644 (file)
@@ -46,8 +46,12 @@ int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **r
 int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
 int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit);
 
-char *xescape(const char *s, const char *bad);
+char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits);
+static inline char *xescape(const char *s, const char *bad) {
+        return xescape_full(s, bad, SIZE_MAX, false);
+}
 char *octescape(const char *s, size_t len);
+char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit);
 
 char *shell_escape(const char *s, const char *bad);
 char* shell_maybe_quote(const char *s, EscapeStyle style);
index 16700013641ccc47c83c115fc22d0387f3989457..b82ca4b21b2e5131d9e6d8c94eded1431449c55c 100644 (file)
@@ -34,7 +34,7 @@ int proc_cmdline(char **ret) {
         }
 
         if (detect_container() > 0)
-                return get_process_cmdline(1, 0, false, ret);
+                return get_process_cmdline(1, SIZE_MAX, 0, ret);
         else
                 return read_one_line_file("/proc/cmdline", ret);
 }
index 052bce6645f396694cac5da55274d792c0547d1f..b50537908cc75362861a665cb4beeaa8c0233847 100644 (file)
 #include "alloc-util.h"
 #include "architecture.h"
 #include "escape.h"
+#include "env-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
 #include "ioprio.h"
+#include "locale-util.h"
 #include "log.h"
 #include "macro.h"
 #include "memory-util.h"
 #include "string-util.h"
 #include "terminal-util.h"
 #include "user-util.h"
+#include "utf8.h"
+
+/* The kernel limits userspace processes to TASK_COMM_LEN (16 bytes), but allows higher values for its own
+ * workers, e.g. "kworker/u9:3-kcryptd/253:0". Let's pick a fixed smallish limit that will work for the kernel.
+ */
+#define COMM_MAX_LEN 128
 
 static int get_process_state(pid_t pid) {
         const char *p;
@@ -80,7 +88,7 @@ int get_process_comm(pid_t pid, char **ret) {
         assert(ret);
         assert(pid >= 0);
 
-        escaped = new(char, TASK_COMM_LEN);
+        escaped = new(char, COMM_MAX_LEN);
         if (!escaped)
                 return -ENOMEM;
 
@@ -93,28 +101,31 @@ int get_process_comm(pid_t pid, char **ret) {
                 return r;
 
         /* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */
-        cellescape(escaped, TASK_COMM_LEN, comm);
+        cellescape(escaped, COMM_MAX_LEN, comm);
 
         *ret = TAKE_PTR(escaped);
         return 0;
 }
 
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
         _cleanup_fclose_ FILE *f = NULL;
-        bool space = false;
-        char *k;
-        _cleanup_free_ char *ans = NULL;
+        _cleanup_free_ char *t = NULL, *ans = NULL;
         const char *p;
-        int c, r;
+        int r;
+        size_t k;
+
+        /* This is supposed to be a safety guard against runaway command lines. */
+        size_t max_length = sc_arg_max();
 
         assert(line);
         assert(pid >= 0);
 
-        /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing
-         * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most
-         * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If
-         * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a
-         * command line that resolves to the empty string will return the "comm" name of the process instead.
+        /* Retrieves a process' command line. Replaces non-utf8 bytes by replacement character (�). If
+         * max_columns is != -1 will return a string of the specified console width at most, abbreviated with
+         * an ellipsis. If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command
+         * line set (the case for kernel threads), or has a command line that resolves to the empty string
+         * will return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of
+         * input data.
          *
          * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
          * comm_fallback is false). Returns 0 and sets *line otherwise. */
@@ -126,130 +137,56 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
         if (r < 0)
                 return r;
 
-        if (max_length == 0) {
-                /* This is supposed to be a safety guard against runaway command lines. */
-                long l = sysconf(_SC_ARG_MAX);
-                assert(l > 0);
-                max_length = l;
-        }
-
-        if (max_length == 1) {
+        /* We assume that each four-byte character uses one or two columns. If we ever check for combining
+         * characters, this assumption will need to be adjusted. */
+        if ((size_t) 4 * max_columns + 1 < max_columns)
+                max_length = MIN(max_length, (size_t) 4 * max_columns + 1);
 
-                /* If there's only room for one byte, return the empty string */
-                ans = new0(char, 1);
-                if (!ans)
-                        return -ENOMEM;
+        t = new(char, max_length);
+        if (!t)
+                return -ENOMEM;
 
-                *line = TAKE_PTR(ans);
-                return 0;
+        k = fread(t, 1, max_length, f);
+        if (k > 0) {
+                /* Arguments are separated by NULs. Let's replace those with spaces. */
+                for (size_t i = 0; i < k - 1; i++)
+                        if (t[i] == '\0')
+                                t[i] = ' ';
 
+                t[k] = '\0'; /* Normally, t[k] is already NUL, so this is just a guard in case of short read */
         } else {
-                bool dotdotdot = false;
-                size_t left;
-
-                ans = new(char, max_length);
-                if (!ans)
-                        return -ENOMEM;
-
-                k = ans;
-                left = max_length;
-                while ((c = getc(f)) != EOF) {
-
-                        if (isprint(c)) {
-
-                                if (space) {
-                                        if (left <= 2) {
-                                                dotdotdot = true;
-                                                break;
-                                        }
-
-                                        *(k++) = ' ';
-                                        left--;
-                                        space = false;
-                                }
-
-                                if (left <= 1) {
-                                        dotdotdot = true;
-                                        break;
-                                }
-
-                                *(k++) = (char) c;
-                                left--;
-                        } else if (k > ans)
-                                space = true;
-                }
-
-                if (dotdotdot) {
-                        if (max_length <= 4) {
-                                k = ans;
-                                left = max_length;
-                        } else {
-                                k = ans + max_length - 4;
-                                left = 4;
-
-                                /* Eat up final spaces */
-                                while (k > ans && isspace(k[-1])) {
-                                        k--;
-                                        left++;
-                                }
-                        }
-
-                        strncpy(k, "...", left-1);
-                        k[left-1] = 0;
-                } else
-                        *k = 0;
-        }
-
-        /* Kernel threads have no argv[] */
-        if (isempty(ans)) {
-                _cleanup_free_ char *t = NULL;
-                int h;
-
-                ans = mfree(ans);
+                /* We only treat getting nothing as an error. We *could* also get an error after reading some
+                 * data, but we ignore that case, as such an error is rather unlikely and we prefer to get
+                 * some data rather than none. */
+                if (ferror(f))
+                        return -errno;
 
-                if (!comm_fallback)
+                if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
                         return -ENOENT;
 
-                h = get_process_comm(pid, &t);
-                if (h < 0)
-                        return h;
-
-                size_t l = strlen(t);
-
-                if (l + 3 <= max_length) {
-                        ans = strjoin("[", t, "]");
-                        if (!ans)
-                                return -ENOMEM;
-
-                } else if (max_length <= 6) {
-                        ans = new(char, max_length);
-                        if (!ans)
-                                return -ENOMEM;
+                /* Kernel threads have no argv[] */
+                _cleanup_free_ char *t2 = NULL;
 
-                        memcpy(ans, "[...]", max_length-1);
-                        ans[max_length-1] = 0;
-                } else {
-                        t[max_length - 6] = 0;
+                r = get_process_comm(pid, &t2);
+                if (r < 0)
+                        return r;
 
-                        /* Chop off final spaces */
-                        delete_trailing_chars(t, WHITESPACE);
+                mfree(t);
+                t = strjoin("[", t2, "]");
+                if (!t)
+                        return -ENOMEM;
+        }
 
-                        ans = strjoin("[", t, "...]");
-                        if (!ans)
-                                return -ENOMEM;
-                }
+        delete_trailing_chars(t, WHITESPACE);
 
-                *line = TAKE_PTR(ans);
-                return 0;
-        }
+        bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8();
 
-        k = realloc(ans, strlen(ans) + 1);
-        if (!k)
+        ans = escape_non_printable_full(t, max_columns, eight_bit);
+        if (!ans)
                 return -ENOMEM;
 
-        ans = NULL;
-        *line = k;
-
+        (void) str_realloc(&ans);
+        *line = TAKE_PTR(ans);
         return 0;
 }
 
@@ -281,7 +218,7 @@ int rename_process(const char name[]) {
          * can use PR_SET_NAME, which sets the thread name for the calling thread. */
         if (prctl(PR_SET_NAME, name) < 0)
                 log_debug_errno(errno, "PR_SET_NAME failed: %m");
-        if (l >= TASK_COMM_LEN) /* Linux process names can be 15 chars at max */
+        if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
                 truncated = true;
 
         /* Second step, change glibc's ID of the process name. */
index 83ba93d0d74aa3b45aa8558ce14bfe85dd025118..2e3bd7250545406d818635ecfd6ca290ed16c173 100644 (file)
                 _r_;                                                    \
         })
 
+typedef enum ProcessCmdlineFlags {
+        PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
+        PROCESS_CMDLINE_USE_LOCALE    = 1 << 1,
+} ProcessCmdlineFlags;
+
 int get_process_comm(pid_t pid, char **name);
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line);
 int get_process_exe(pid_t pid, char **name);
 int get_process_uid(pid_t pid, uid_t *uid);
 int get_process_gid(pid_t pid, gid_t *gid);
index a630856236a9a699be23f14a12d7cdbaadd4ff9d..47b17c9d3e7c07f30ff3687ab8a27580c0973b1c 100644 (file)
@@ -257,3 +257,16 @@ static inline void *memory_startswith_no_case(const void *p, size_t sz, const ch
 
         return (uint8_t*) p + n;
 }
+
+static inline char* str_realloc(char **p) {
+        /* Reallocate *p to actual size */
+
+        if (!*p)
+                return NULL;
+
+        char *t = realloc(*p, strlen(*p) + 1);
+        if (!t)
+                return NULL;
+
+        return (*p = t);
+}
index 090c69d1400bad8bd43b33ddcd7df536e131a2d3..afc24700dd77a786d8cf9392916dece1b3785c3a 100644 (file)
@@ -32,6 +32,7 @@
 #include "gunicode.h"
 #include "hexdecoct.h"
 #include "macro.h"
+#include "string-util.h"
 #include "utf8.h"
 
 bool unichar_is_valid(char32_t ch) {
@@ -192,46 +193,93 @@ char *utf8_escape_invalid(const char *str) {
         }
 
         *s = '\0';
-
+        (void) str_realloc(&p);
         return p;
 }
 
-char *utf8_escape_non_printable(const char *str) {
-        char *p, *s;
+static int utf8_char_console_width(const char *str) {
+        char32_t c;
+        int r;
+
+        r = utf8_encoded_to_unichar(str, &c);
+        if (r < 0)
+                return r;
+
+        /* TODO: we should detect combining characters */
+
+        return unichar_iswide(c) ? 2 : 1;
+}
+
+char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
+        char *p, *s, *prev_s;
+        size_t n = 0; /* estimated print width */
 
         assert(str);
 
-        p = s = malloc(strlen(str) * 4 + 1);
+        if (console_width == 0)
+                return strdup("");
+
+        p = s = prev_s = malloc(strlen(str) * 4 + 1);
         if (!p)
                 return NULL;
 
-        while (*str) {
+        for (;;) {
                 int len;
+                char *saved_s = s;
+
+                if (!*str) /* done! */
+                        goto finish;
 
                 len = utf8_encoded_valid_unichar(str, (size_t) -1);
                 if (len > 0) {
                         if (utf8_is_printable(str, len)) {
+                                int w;
+
+                                w = utf8_char_console_width(str);
+                                assert(w >= 0);
+                                if (n + w > console_width)
+                                        goto truncation;
+
                                 s = mempcpy(s, str, len);
                                 str += len;
+                                n += w;
+
                         } else {
-                                while (len > 0) {
+                                for (; len > 0; len--) {
+                                        if (n + 4 > console_width)
+                                                goto truncation;
+
                                         *(s++) = '\\';
                                         *(s++) = 'x';
                                         *(s++) = hexchar((int) *str >> 4);
                                         *(s++) = hexchar((int) *str);
 
                                         str += 1;
-                                        len--;
+                                        n += 4;
                                 }
                         }
                 } else {
-                        s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
+                        if (n + 1 > console_width)
+                                goto truncation;
+
+                        s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
                         str += 1;
+                        n += 1;
                 }
+
+                prev_s = saved_s;
         }
 
-        *s = '\0';
+ truncation:
+        /* Try to go back one if we don't have enough space for the ellipsis */
+        if (n + 1 >= console_width)
+                s = prev_s;
+
+        s = mempcpy(s, "…", strlen("…"));
 
+ finish:
+        *s = '\0';
+        (void) str_realloc(&p);
         return p;
 }
 
@@ -519,15 +567,15 @@ size_t utf8_console_width(const char *str) {
         /* Returns the approximate width a string will take on screen when printed on a character cell
          * terminal/console. */
 
-        while (*str != 0) {
-                char32_t c;
+        while (*str) {
+                int w;
 
-                if (utf8_encoded_to_unichar(str, &c) < 0)
+                w = utf8_char_console_width(str);
+                if (w < 0)
                         return (size_t) -1;
 
+                n += w;
                 str = utf8_next_char(str);
-
-                n += unichar_iswide(c) ? 2 : 1;
         }
 
         return n;
index 6df70921dbd9050c6292622d8f29ab0be57960ae..62e99b72802d0bae2b2c2bc3457dfabdb5813661 100644 (file)
@@ -22,7 +22,10 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pu
 #define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
 
 char *utf8_escape_invalid(const char *s);
-char *utf8_escape_non_printable(const char *str);
+char *utf8_escape_non_printable_full(const char *str, size_t console_width);
+static inline char *utf8_escape_non_printable(const char *str) {
+        return utf8_escape_non_printable_full(str, (size_t) -1);
+}
 
 size_t utf8_encode_unichar(char *out_utf8, char32_t g);
 size_t utf16_encode_unichar(char16_t *out, char32_t c);
index c548be07ca103162af8e45767bf8e020aee95a74..fff6b505cc36fe3ba6c04284ea95eb96e4ca760d 100644 (file)
@@ -930,7 +930,7 @@ static int run(int argc, char *argv[]) {
         r = show_cgroup_get_path_and_warn(arg_machine, arg_root, &root);
         if (r < 0)
                 return log_error_errno(r, "Failed to get root control group path: %m");
-        log_debug("Cgroup path: %s", root);
+        log_debug("CGroup path: %s", root);
 
         a = hashmap_new(&group_hash_ops);
         b = hashmap_new(&group_hash_ops);
index 16b4a6b133267d244db69c8413bcc75d6aabd0a1..a60362fff65c56cc98eef39f89c44af6f5281a54 100644 (file)
@@ -901,7 +901,7 @@ static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *
                 p = buf;
         }
 
-        (void) get_process_cmdline(pid, 0, true, &cmdline);
+        (void) get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &cmdline);
 
         return sd_bus_message_append(reply,
                                      "(sus)",
index 9ab704e20762d439cf22d5bd09646ed0f6e017b4..2cceb49f4b1aa05b51a210cb357f6dd81fa0d29a 100644 (file)
@@ -661,7 +661,7 @@ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) {
         if (r < 0)
                 return r;
 
-        r = get_process_cmdline(container_pid, 0, false, cmdline);
+        r = get_process_cmdline(container_pid, SIZE_MAX, 0, cmdline);
         if (r < 0)
                 return r;
 
@@ -1154,7 +1154,7 @@ static int gather_pid_metadata(
         if (sd_pid_get_slice(pid, &t) >= 0)
                 set_iovec_field_free(iovec, n_iovec, "COREDUMP_SLICE=", t);
 
-        if (get_process_cmdline(pid, 0, false, &t) >= 0)
+        if (get_process_cmdline(pid, SIZE_MAX, 0, &t) >= 0)
                 set_iovec_field_free(iovec, n_iovec, "COREDUMP_CMDLINE=", t);
 
         if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0)
index e6aeb03b77974a7ffd2603843dfc474666244028..46edf24cd6500e8ab606189a619e084133cb91fe 100644 (file)
@@ -7,6 +7,7 @@
 #include "alloc-util.h"
 #include "audit-util.h"
 #include "cgroup-util.h"
+#include "env-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -76,18 +77,14 @@ static size_t cache_max(void) {
                 if (r < 0) {
                         log_warning_errno(r, "Cannot query /proc/meminfo for MemTotal: %m");
                         cached = CACHE_MAX_FALLBACK;
-                } else {
+                } else
                         /* Cache entries are usually a few kB, but the process cmdline is controlled by the
                          * user and can be up to _SC_ARG_MAX, usually 2MB. Let's say that approximately up to
                          * 1/8th of memory may be used by the cache.
                          *
                          * In the common case, this formula gives 64 cache entries for each GB of RAM.
                          */
-                        long l = sysconf(_SC_ARG_MAX);
-                        assert(l > 0);
-
-                        cached = CLAMP(mem_total / 8 / (uint64_t) l, CACHE_MAX_MIN, CACHE_MAX_MAX);
-                }
+                        cached = CLAMP(mem_total / 8 / sc_arg_max(), CACHE_MAX_MIN, CACHE_MAX_MAX);
         }
 
         return cached;
@@ -233,7 +230,7 @@ static void client_context_read_basic(ClientContext *c) {
         if (get_process_exe(c->pid, &t) >= 0)
                 free_and_replace(c->exe, t);
 
-        if (get_process_cmdline(c->pid, 0, false, &t) >= 0)
+        if (get_process_cmdline(c->pid, SIZE_MAX, 0, &t) >= 0)
                 free_and_replace(c->cmdline, t);
 
         if (get_process_capeff(c->pid, &t) >= 0)
index 91a243944c78b9fa914abce1be4fec838c0f0399..e6fdcfa277deddfbfca7c1001b57058b72ad3ab4 100644 (file)
@@ -29,7 +29,7 @@ static void show_pid_array(
                 pid_t pids[],
                 unsigned n_pids,
                 const char *prefix,
-                unsigned n_columns,
+                size_t n_columns,
                 bool extra,
                 bool more,
                 OutputFlags flags) {
@@ -51,17 +51,19 @@ static void show_pid_array(
         pid_width = DECIMAL_STR_WIDTH(pids[j]);
 
         if (flags & OUTPUT_FULL_WIDTH)
-                n_columns = 0;
+                n_columns = SIZE_MAX;
         else {
-                if (n_columns > pid_width+2)
-                        n_columns -= pid_width+2;
+                if (n_columns > pid_width + 3) /* something like "├─1114784 " */
+                        n_columns -= pid_width + 3;
                 else
                         n_columns = 20;
         }
         for (i = 0; i < n_pids; i++) {
                 _cleanup_free_ char *t = NULL;
 
-                (void) get_process_cmdline(pids[i], n_columns, true, &t);
+                (void) get_process_cmdline(pids[i], n_columns,
+                                           PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_USE_LOCALE,
+                                           &t);
 
                 if (extra)
                         printf("%s%s ", prefix, special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
@@ -75,7 +77,7 @@ static void show_pid_array(
 static int show_cgroup_one_by_path(
                 const char *path,
                 const char *prefix,
-                unsigned n_columns,
+                size_t n_columns,
                 bool more,
                 OutputFlags flags) {
 
@@ -119,7 +121,7 @@ static int show_cgroup_one_by_path(
 int show_cgroup_by_path(
                 const char *path,
                 const char *prefix,
-                unsigned n_columns,
+                size_t n_columns,
                 OutputFlags flags) {
 
         _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
@@ -199,7 +201,7 @@ int show_cgroup_by_path(
 int show_cgroup(const char *controller,
                 const char *path,
                 const char *prefix,
-                unsigned n_columns,
+                size_t n_columns,
                 OutputFlags flags) {
         _cleanup_free_ char *p = NULL;
         int r;
@@ -217,7 +219,7 @@ static int show_extra_pids(
                 const char *controller,
                 const char *path,
                 const char *prefix,
-                unsigned n_columns,
+                size_t n_columns,
                 const pid_t pids[],
                 unsigned n_pids,
                 OutputFlags flags) {
@@ -262,7 +264,7 @@ int show_cgroup_and_extra(
                 const char *controller,
                 const char *path,
                 const char *prefix,
-                unsigned n_columns,
+                size_t n_columns,
                 const pid_t extra_pids[],
                 unsigned n_extra_pids,
                 OutputFlags flags) {
index 3593e9dcf43adcc9a1a0f371e2bf604b90a73785..dfb26f82910f82e2f45298c4953301263f994a7f 100644 (file)
@@ -9,10 +9,10 @@
 #include "logs-show.h"
 #include "output-mode.h"
 
-int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags);
-int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags);
+int show_cgroup_by_path(const char *path, const char *prefix, size_t n_columns, OutputFlags flags);
+int show_cgroup(const char *controller, const char *path, const char *prefix, size_t n_columns, OutputFlags flags);
 
-int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
+int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, size_t n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
 
 int show_cgroup_get_unit_path_and_warn(
                 sd_bus *bus,
index 4ee4aa974d73272dcc845ccd62b7e7ae54cf6ad2..add17f9547dc6b803143d4cef265f1a8f54167a2 100644 (file)
@@ -6,10 +6,45 @@
 #include "tests.h"
 
 static void test_cescape(void) {
-        _cleanup_free_ char *escaped;
+        _cleanup_free_ char *t;
 
-        assert_se(escaped = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313"));
-        assert_se(streq(escaped, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313"));
+        assert_se(t = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313"));
+        assert_se(streq(t, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313"));
+}
+
+static void test_xescape(void) {
+        _cleanup_free_ char *t;
+
+        assert_se(t = xescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", ""));
+        assert_se(streq(t, "abc\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb"));
+}
+
+static void test_xescape_full(bool eight_bits) {
+        const char* escaped = !eight_bits ?
+                "a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb" :
+                "a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\177\234\313";
+        const unsigned full_fit = !eight_bits ? 55 : 46;
+
+        for (unsigned i = 0; i < 60; i++) {
+                _cleanup_free_ char *t;
+
+                assert_se(t = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i, eight_bits));
+
+                log_info("%02d: %s", i, t);
+
+                if (i >= full_fit)
+                        assert_se(streq(t, escaped));
+                else if (i >= 3) {
+                        /* We need up to four columns, so up to three three columns may be wasted */
+                        assert_se(strlen(t) == i || strlen(t) == i - 1 || strlen(t) == i - 2 || strlen(t) == i - 3);
+                        assert_se(strneq(t, escaped, i - 3) || strneq(t, escaped, i - 4) ||
+                                  strneq(t, escaped, i - 5) || strneq(t, escaped, i - 6));
+                        assert_se(endswith(t, "..."));
+                } else {
+                        assert_se(strlen(t) == i);
+                        assert_se(strneq(t, "...", i));
+                }
+        }
 }
 
 static void test_cunescape(void) {
@@ -123,6 +158,9 @@ int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
         test_cescape();
+        test_xescape();
+        test_xescape_full(false);
+        test_xescape_full(true);
         test_cunescape();
         test_shell_escape();
         test_shell_maybe_quote();
index 89f6618e2e85280bb1ea6a9ceb4eeae5e001fafc..9b644c09bbfc3236eebc00beb765a62b1348515a 100644 (file)
@@ -48,14 +48,14 @@ static void test_get_process_comm(pid_t pid) {
         } else
                 log_warning("%s not exist.", path);
 
-        assert_se(get_process_cmdline(pid, 0, true, &c) >= 0);
+        assert_se(get_process_cmdline(pid, 0, PROCESS_CMDLINE_COMM_FALLBACK, &c) >= 0);
         log_info("PID"PID_FMT" cmdline: '%s'", pid, c);
 
-        assert_se(get_process_cmdline(pid, 8, false, &d) >= 0);
+        assert_se(get_process_cmdline(pid, 8, 0, &d) >= 0);
         log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);
 
         free(d);
-        assert_se(get_process_cmdline(pid, 1, false, &d) >= 0);
+        assert_se(get_process_cmdline(pid, 1, 0, &d) >= 0);
         log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
 
         assert_se(get_process_ppid(pid, &e) >= 0);
@@ -107,12 +107,12 @@ static void test_get_process_comm_escape(void) {
         test_get_process_comm_escape_one("foo", "foo");
         test_get_process_comm_escape_one("012345678901234", "012345678901234");
         test_get_process_comm_escape_one("0123456789012345", "012345678901234");
-        test_get_process_comm_escape_one("äöüß", "\\303\\244\\303");
-        test_get_process_comm_escape_one("xäöüß", "x\\303\\244");
-        test_get_process_comm_escape_one("xxäöüß", "xx\\303\\244");
-        test_get_process_comm_escape_one("xxxäöüß", "xxx\\303\\244");
-        test_get_process_comm_escape_one("xxxxäöüß", "xxxx\\303\\244");
-        test_get_process_comm_escape_one("xxxxxäöüß", "xxxxx\\303");
+        test_get_process_comm_escape_one("äöüß", "\\303\\244\\303\\266\\303\\274\\303\\237");
+        test_get_process_comm_escape_one("xäöüß", "x\\303\\244\\303\\266\\303\\274\\303\\237");
+        test_get_process_comm_escape_one("xxäöüß", "xx\\303\\244\\303\\266\\303\\274\\303\\237");
+        test_get_process_comm_escape_one("xxxäöüß", "xxx\\303\\244\\303\\266\\303\\274\\303\\237");
+        test_get_process_comm_escape_one("xxxxäöüß", "xxxx\\303\\244\\303\\266\\303\\274\\303\\237");
+        test_get_process_comm_escape_one("xxxxxäöüß", "xxxxx\\303\\244\\303\\266\\303\\274\\303\\237");
 
         assert_se(prctl(PR_SET_NAME, saved) >= 0);
 }
@@ -237,150 +237,149 @@ static void test_get_process_cmdline_harder(void) {
 
         assert_se(prctl(PR_SET_NAME, "testa") >= 0);
 
-        assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
+        assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) == -ENOENT);
 
-        assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         assert_se(streq(line, "[testa]"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), 0, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        log_info("'%s'", line);
         assert_se(streq(line, ""));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
-        assert_se(streq(line, "["));
+        assert_se(get_process_cmdline(getpid_cached(), 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, ""));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
-        assert_se(streq(line, "[."));
+        assert_se(get_process_cmdline(getpid_cached(), 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "["));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
-        assert_se(streq(line, "[.."));
+        assert_se(get_process_cmdline(getpid_cached(), 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "[t…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
-        assert_se(streq(line, "[..."));
+        assert_se(get_process_cmdline(getpid_cached(), 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "[te…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
-        assert_se(streq(line, "[...]"));
+        assert_se(get_process_cmdline(getpid_cached(), 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "[tes…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
-        assert_se(streq(line, "[t...]"));
+        assert_se(get_process_cmdline(getpid_cached(), 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "[test…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         assert_se(streq(line, "[testa]"));
         line = mfree(line);
 
-        assert_se(write(fd, "\0\0\0\0\0\0\0\0\0", 10) == 10);
-
-        assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
-
-        assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         assert_se(streq(line, "[testa]"));
         line = mfree(line);
 
-        assert_se(write(fd, "foo\0bar\0\0\0\0\0", 10) == 10);
+        assert_se(write(fd, "foo\0bar", 8) == 8);
 
-        assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) >= 0);
+        log_info("'%s'", line);
         assert_se(streq(line, "foo bar"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         assert_se(streq(line, "foo bar"));
         line = mfree(line);
 
         assert_se(write(fd, "quux", 4) == 4);
-        assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) >= 0);
+        log_info("'%s'", line);
         assert_se(streq(line, "foo bar quux"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         assert_se(streq(line, "foo bar quux"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 1, true, &line) >= 0);
-        assert_se(streq(line, ""));
+        assert_se(get_process_cmdline(getpid_cached(), 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, ""));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 2, true, &line) >= 0);
-        assert_se(streq(line, "."));
+        assert_se(get_process_cmdline(getpid_cached(), 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "f…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 3, true, &line) >= 0);
-        assert_se(streq(line, ".."));
+        assert_se(get_process_cmdline(getpid_cached(), 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "fo…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 4, true, &line) >= 0);
-        assert_se(streq(line, "..."));
+        assert_se(get_process_cmdline(getpid_cached(), 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "foo…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 5, true, &line) >= 0);
-        assert_se(streq(line, "f..."));
+        assert_se(get_process_cmdline(getpid_cached(), 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "foo …"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 6, true, &line) >= 0);
-        assert_se(streq(line, "fo..."));
+        assert_se(get_process_cmdline(getpid_cached(), 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "foo b…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 7, true, &line) >= 0);
-        assert_se(streq(line, "foo..."));
+        assert_se(get_process_cmdline(getpid_cached(), 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "foo ba…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 8, true, &line) >= 0);
-        assert_se(streq(line, "foo..."));
+        assert_se(get_process_cmdline(getpid_cached(), 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "foo bar…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 9, true, &line) >= 0);
-        assert_se(streq(line, "foo b..."));
+        assert_se(get_process_cmdline(getpid_cached(), 9, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "foo bar …"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
-        assert_se(streq(line, "foo ba..."));
+        assert_se(get_process_cmdline(getpid_cached(), 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "foo bar q…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
-        assert_se(streq(line, "foo bar..."));
+        assert_se(get_process_cmdline(getpid_cached(), 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "foo bar qu…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
-        assert_se(streq(line, "foo bar..."));
+        assert_se(get_process_cmdline(getpid_cached(), 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "foo bar quux"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 13, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), 13, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         assert_se(streq(line, "foo bar quux"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 14, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), 14, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         assert_se(streq(line, "foo bar quux"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 1000, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         assert_se(streq(line, "foo bar quux"));
         line = mfree(line);
 
         assert_se(ftruncate(fd, 0) >= 0);
         assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
 
-        assert_se(get_process_cmdline(getpid_cached(), 0, false, &line) == -ENOENT);
+        assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) == -ENOENT);
 
-        assert_se(get_process_cmdline(getpid_cached(), 0, true, &line) >= 0);
+        assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
         assert_se(streq(line, "[aaaa bbbb cccc]"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 10, true, &line) >= 0);
-        assert_se(streq(line, "[aaaa...]"));
+        assert_se(get_process_cmdline(getpid_cached(), 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "[aaaa bbb…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 11, true, &line) >= 0);
-        assert_se(streq(line, "[aaaa...]"));
+        assert_se(get_process_cmdline(getpid_cached(), 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "[aaaa bbbb…"));
         line = mfree(line);
 
-        assert_se(get_process_cmdline(getpid_cached(), 12, true, &line) >= 0);
-        assert_se(streq(line, "[aaaa b...]"));
+        assert_se(get_process_cmdline(getpid_cached(), 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
+        assert_se(streq(line, "[aaaa bbbb …"));
         line = mfree(line);
 
         safe_close(fd);
@@ -408,8 +407,10 @@ static void test_rename_process_now(const char *p, int ret) {
         assert_se(get_process_comm(0, &comm) >= 0);
         log_info("comm = <%s>", comm);
         assert_se(strneq(comm, p, TASK_COMM_LEN-1));
+        /* We expect comm to be at most 16 bytes (TASK_COMM_LEN). The kernel may raise this limit in the
+         * future. We'd only check the initial part, at least until we recompile, but this will still pass. */
 
-        r = get_process_cmdline(0, 0, false, &cmdline);
+        r = get_process_cmdline(0, SIZE_MAX, 0, &cmdline);
         assert_se(r >= 0);
         /* we cannot expect cmdline to be renamed properly without privileges */
         if (geteuid() == 0) {
index d1e48da2a665d0a0a229da3963abb61461cfaedb..b5c4e3dc343c5714eb19b4bf844d7f12713b3848 100644 (file)
@@ -7,6 +7,8 @@
 #include "util.h"
 
 static void test_utf8_is_printable(void) {
+        log_info("/* %s */", __func__);
+
         assert_se(utf8_is_printable("ascii is valid\tunicode", 22));
         assert_se(utf8_is_printable("\342\204\242", 3));
         assert_se(!utf8_is_printable("\341\204", 2));
@@ -14,18 +16,24 @@ static void test_utf8_is_printable(void) {
 }
 
 static void test_utf8_is_valid(void) {
+        log_info("/* %s */", __func__);
+
         assert_se(utf8_is_valid("ascii is valid unicode"));
         assert_se(utf8_is_valid("\342\204\242"));
         assert_se(!utf8_is_valid("\341\204"));
 }
 
 static void test_ascii_is_valid(void) {
+        log_info("/* %s */", __func__);
+
         assert_se( ascii_is_valid("alsdjf\t\vbarr\nba z"));
         assert_se(!ascii_is_valid("\342\204\242"));
         assert_se(!ascii_is_valid("\341\204"));
 }
 
 static void test_ascii_is_valid_n(void) {
+        log_info("/* %s */", __func__);
+
         assert_se( ascii_is_valid_n("alsdjf\t\vbarr\nba z", 17));
         assert_se( ascii_is_valid_n("alsdjf\t\vbarr\nba z", 16));
         assert_se(!ascii_is_valid_n("alsdjf\t\vbarr\nba z", 18));
@@ -36,6 +44,8 @@ static void test_ascii_is_valid_n(void) {
 }
 
 static void test_utf8_encoded_valid_unichar(void) {
+        log_info("/* %s */", __func__);
+
         assert_se(utf8_encoded_valid_unichar("\342\204\242", 1) == -EINVAL); /* truncated */
         assert_se(utf8_encoded_valid_unichar("\342\204\242", 2) == -EINVAL); /* truncated */
         assert_se(utf8_encoded_valid_unichar("\342\204\242", 3) == 3);
@@ -53,9 +63,11 @@ static void test_utf8_encoded_valid_unichar(void) {
         assert_se(utf8_encoded_valid_unichar("\341\204\341\204", 5) == -EINVAL);
 }
 
-static void test_utf8_escaping(void) {
+static void test_utf8_escape_invalid(void) {
         _cleanup_free_ char *p1, *p2, *p3;
 
+        log_info("/* %s */", __func__);
+
         p1 = utf8_escape_invalid("goo goo goo");
         puts(p1);
         assert_se(utf8_is_valid(p1));
@@ -69,9 +81,11 @@ static void test_utf8_escaping(void) {
         assert_se(utf8_is_valid(p3));
 }
 
-static void test_utf8_escaping_printable(void) {
+static void test_utf8_escape_non_printable(void) {
         _cleanup_free_ char *p1, *p2, *p3, *p4, *p5, *p6;
 
+        log_info("/* %s */", __func__);
+
         p1 = utf8_escape_non_printable("goo goo goo");
         puts(p1);
         assert_se(utf8_is_valid(p1));
@@ -97,12 +111,45 @@ static void test_utf8_escaping_printable(void) {
         assert_se(utf8_is_valid(p6));
 }
 
+static void test_utf8_escape_non_printable_full(void) {
+        log_info("/* %s */", __func__);
+
+        for (size_t i = 0; i < 20; i++) {
+                _cleanup_free_ char *p;
+
+                p = utf8_escape_non_printable_full("goo goo goo", i);
+                puts(p);
+                assert_se(utf8_is_valid(p));
+                assert_se(utf8_console_width(p) <= i);
+        }
+
+        for (size_t i = 0; i < 20; i++) {
+                _cleanup_free_ char *p;
+
+                p = utf8_escape_non_printable_full("\001 \019\20\a", i);
+                puts(p);
+                assert_se(utf8_is_valid(p));
+                assert_se(utf8_console_width(p) <= i);
+        }
+
+        for (size_t i = 0; i < 20; i++) {
+                _cleanup_free_ char *p;
+
+                p = utf8_escape_non_printable_full("\xef\xbf\x30\x13", i);
+                puts(p);
+                assert_se(utf8_is_valid(p));
+                assert_se(utf8_console_width(p) <= i);
+        }
+}
+
 static void test_utf16_to_utf8(void) {
         const char16_t utf16[] = { htole16('a'), htole16(0xd800), htole16('b'), htole16(0xdc00), htole16('c'), htole16(0xd801), htole16(0xdc37) };
         static const char utf8[] = { 'a', 'b', 'c', 0xf0, 0x90, 0x90, 0xb7 };
         _cleanup_free_ char16_t *b = NULL;
         _cleanup_free_ char *a = NULL;
 
+        log_info("/* %s */", __func__);
+
         /* Convert UTF-16 to UTF-8, filtering embedded bad chars */
         a = utf16_to_utf8(utf16, sizeof(utf16));
         assert_se(a);
@@ -120,6 +167,8 @@ static void test_utf16_to_utf8(void) {
 }
 
 static void test_utf8_n_codepoints(void) {
+        log_info("/* %s */", __func__);
+
         assert_se(utf8_n_codepoints("abc") == 3);
         assert_se(utf8_n_codepoints("zażółcić gęślą jaźń") == 19);
         assert_se(utf8_n_codepoints("串") == 1);
@@ -129,6 +178,8 @@ static void test_utf8_n_codepoints(void) {
 }
 
 static void test_utf8_console_width(void) {
+        log_info("/* %s */", __func__);
+
         assert_se(utf8_console_width("abc") == 3);
         assert_se(utf8_console_width("zażółcić gęślą jaźń") == 19);
         assert_se(utf8_console_width("串") == 2);
@@ -140,6 +191,8 @@ static void test_utf8_console_width(void) {
 static void test_utf8_to_utf16(void) {
         const char *p;
 
+        log_info("/* %s */", __func__);
+
         FOREACH_STRING(p,
                        "abc",
                        "zażółcić gęślą jaźń",
@@ -165,8 +218,9 @@ int main(int argc, char *argv[]) {
         test_ascii_is_valid();
         test_ascii_is_valid_n();
         test_utf8_encoded_valid_unichar();
-        test_utf8_escaping();
-        test_utf8_escaping_printable();
+        test_utf8_escape_invalid();
+        test_utf8_escape_non_printable();
+        test_utf8_escape_non_printable_full();
         test_utf16_to_utf8();
         test_utf8_n_codepoints();
         test_utf8_console_width();
index bd85b92caa5cca76a7698619548b43984b87fe08..8252c4b2aaf47b6f34ce3b71c4c76227ab2eebaf 100755 (executable)
@@ -98,7 +98,7 @@ function run {
         return 0
     fi
     if [[ "$2" = "yes" && "$is_cgns_supported" = "no" ]];  then
-        printf "Cgroup namespaces are not supported. Skipping.\n" >&2
+        printf "CGroup namespaces are not supported. Skipping.\n" >&2
         return 0
     fi