diff options
author | Lennart Poettering <lennart@poettering.net> | 2019-05-24 10:41:30 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-24 10:41:30 +0200 |
commit | 05332e243cfcea70f900a741b917d04291a1f9e0 (patch) | |
tree | 880e2d5d1c55fc79b9b402598eafdcf7b27f92b9 | |
parent | Drop support for /usr/sbin/halt.local (diff) | |
parent | basic/utf8: reduce memory usage (diff) | |
download | systemd-05332e243cfcea70f900a741b917d04291a1f9e0.tar.xz systemd-05332e243cfcea70f900a741b917d04291a1f9e0.zip |
Merge pull request #12590 from keszybz/unicode-cmdlines
Use unicode for cmdline printing
-rw-r--r-- | TODO | 5 | ||||
-rw-r--r-- | src/basic/env-util.c | 4 | ||||
-rw-r--r-- | src/basic/env-util.h | 7 | ||||
-rw-r--r-- | src/basic/escape.c | 71 | ||||
-rw-r--r-- | src/basic/escape.h | 6 | ||||
-rw-r--r-- | src/basic/proc-cmdline.c | 2 | ||||
-rw-r--r-- | src/basic/process-util.c | 183 | ||||
-rw-r--r-- | src/basic/process-util.h | 7 | ||||
-rw-r--r-- | src/basic/string-util.h | 13 | ||||
-rw-r--r-- | src/basic/utf8.c | 76 | ||||
-rw-r--r-- | src/basic/utf8.h | 5 | ||||
-rw-r--r-- | src/cgtop/cgtop.c | 2 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 2 | ||||
-rw-r--r-- | src/coredump/coredump.c | 4 | ||||
-rw-r--r-- | src/journal/journald-context.c | 11 | ||||
-rw-r--r-- | src/shared/cgroup-show.c | 22 | ||||
-rw-r--r-- | src/shared/cgroup-show.h | 6 | ||||
-rw-r--r-- | src/test/test-escape.c | 44 | ||||
-rw-r--r-- | src/test/test-process-util.c | 143 | ||||
-rw-r--r-- | src/test/test-utf8.c | 62 | ||||
-rwxr-xr-x | test/TEST-13-NSPAWN-SMOKE/test.sh | 2 |
21 files changed, 418 insertions, 259 deletions
@@ -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. diff --git a/src/basic/env-util.c b/src/basic/env-util.c index fd449dcce07..896eec58356 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -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; diff --git a/src/basic/env-util.h b/src/basic/env-util.h index d54f99658bd..92802ed7744 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -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); diff --git a/src/basic/escape.c b/src/basic/escape.c index 5f715156fbf..33a6f204f55 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -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) { diff --git a/src/basic/escape.h b/src/basic/escape.h index 515620993d0..b26054c5df8 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -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); diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index 16700013641..b82ca4b21b2 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -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); } diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 052bce6645f..b50537908cc 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -25,10 +25,12 @@ #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" @@ -43,6 +45,12 @@ #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. */ diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 83ba93d0d74..2e3bd725054 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -31,8 +31,13 @@ _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); diff --git a/src/basic/string-util.h b/src/basic/string-util.h index a630856236a..47b17c9d3e7 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -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); +} diff --git a/src/basic/utf8.c b/src/basic/utf8.c index 090c69d1400..afc24700dd7 100644 --- a/src/basic/utf8.c +++ b/src/basic/utf8.c @@ -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; diff --git a/src/basic/utf8.h b/src/basic/utf8.h index 6df70921dbd..62e99b72802 100644 --- a/src/basic/utf8.h +++ b/src/basic/utf8.h @@ -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); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index c548be07ca1..fff6b505cc3 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -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); diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 16b4a6b1332..a60362fff65 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -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)", diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 9ab704e2076..2cceb49f4b1 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -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) diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index e6aeb03b779..46edf24cd65 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -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) diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 91a243944c7..e6fdcfa277d 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -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) { diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index 3593e9dcf43..dfb26f82910 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -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, diff --git a/src/test/test-escape.c b/src/test/test-escape.c index 4ee4aa974d7..add17f9547d 100644 --- a/src/test/test-escape.c +++ b/src/test/test-escape.c @@ -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(); diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 89f6618e2e8..9b644c09bbf 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -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) { diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c index d1e48da2a66..b5c4e3dc343 100644 --- a/src/test/test-utf8.c +++ b/src/test/test-utf8.c @@ -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(); diff --git a/test/TEST-13-NSPAWN-SMOKE/test.sh b/test/TEST-13-NSPAWN-SMOKE/test.sh index bd85b92caa5..8252c4b2aaf 100755 --- a/test/TEST-13-NSPAWN-SMOKE/test.sh +++ b/test/TEST-13-NSPAWN-SMOKE/test.sh @@ -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 |