From 22810041c2200fe72b0e0c985d0e404f8b80f9e2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Nov 2019 14:49:40 +0100 Subject: [PATCH 01/11] parse-util: sometimes it is useful to check if a string is a valid integer, but not actually parse it --- src/basic/parse-util.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index aec6099c9c..b81db04989 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -365,7 +365,6 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { unsigned long l; assert(s); - assert(ret_u); assert(base <= 16); /* strtoul() is happy to parse negative values, and silently @@ -389,7 +388,9 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { if ((unsigned long) (unsigned) l != l) return -ERANGE; - *ret_u = (unsigned) l; + if (ret_u) + *ret_u = (unsigned) l; + return 0; } @@ -398,7 +399,6 @@ int safe_atoi(const char *s, int *ret_i) { long l; assert(s); - assert(ret_i); errno = 0; l = strtol(s, &x, 0); @@ -409,7 +409,9 @@ int safe_atoi(const char *s, int *ret_i) { if ((long) (int) l != l) return -ERANGE; - *ret_i = (int) l; + if (ret_i) + *ret_i = (int) l; + return 0; } @@ -418,7 +420,6 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { unsigned long long l; assert(s); - assert(ret_llu); s += strspn(s, WHITESPACE); @@ -431,7 +432,9 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { if (*s == '-') return -ERANGE; - *ret_llu = l; + if (ret_llu) + *ret_llu = l; + return 0; } @@ -440,7 +443,6 @@ int safe_atolli(const char *s, long long int *ret_lli) { long long l; assert(s); - assert(ret_lli); errno = 0; l = strtoll(s, &x, 0); @@ -449,7 +451,9 @@ int safe_atolli(const char *s, long long int *ret_lli) { if (!x || x == s || *x != 0) return -EINVAL; - *ret_lli = l; + if (ret_lli) + *ret_lli = l; + return 0; } @@ -458,7 +462,6 @@ int safe_atou8(const char *s, uint8_t *ret) { unsigned long l; assert(s); - assert(ret); s += strspn(s, WHITESPACE); @@ -473,7 +476,8 @@ int safe_atou8(const char *s, uint8_t *ret) { if ((unsigned long) (uint8_t) l != l) return -ERANGE; - *ret = (uint8_t) l; + if (ret) + *ret = (uint8_t) l; return 0; } @@ -507,7 +511,6 @@ int safe_atoi16(const char *s, int16_t *ret) { long l; assert(s); - assert(ret); errno = 0; l = strtol(s, &x, 0); @@ -518,7 +521,9 @@ int safe_atoi16(const char *s, int16_t *ret) { if ((long) (int16_t) l != l) return -ERANGE; - *ret = (int16_t) l; + if (ret) + *ret = (int16_t) l; + return 0; } @@ -528,7 +533,6 @@ int safe_atod(const char *s, double *ret_d) { double d = 0; assert(s); - assert(ret_d); loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); if (loc == (locale_t) 0) @@ -541,7 +545,9 @@ int safe_atod(const char *s, double *ret_d) { if (!x || x == s || *x != 0) return -EINVAL; - *ret_d = (double) d; + if (ret_d) + *ret_d = (double) d; + return 0; } From b64cea60275b2921d84fb9b7c68c61214f405326 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Aug 2019 18:06:03 +0200 Subject: [PATCH 02/11] ordered-set: add ordered_set_first() helper --- src/basic/ordered-set.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index ba43451e27..383a729cab 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -50,6 +50,10 @@ static inline void* ordered_set_remove(OrderedSet *s, void *p) { return ordered_hashmap_remove((OrderedHashmap*) s, p); } +static inline void* ordered_set_first(OrderedSet *s) { + return ordered_hashmap_first((OrderedHashmap*) s); +} + static inline void* ordered_set_steal_first(OrderedSet *s) { return ordered_hashmap_steal_first((OrderedHashmap*) s); } From 9933a47808437ddf50de51b21a66d1f4821efc2a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 15 Jul 2019 13:32:03 +0200 Subject: [PATCH 03/11] errno-util: add new ERRNO_IS_DISK_SPACE() helper --- src/basic/errno-util.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/basic/errno-util.h b/src/basic/errno-util.h index 8f1be6c00e..65a6384eeb 100644 --- a/src/basic/errno-util.h +++ b/src/basic/errno-util.h @@ -101,3 +101,11 @@ static inline bool ERRNO_IS_PRIVILEGE(int r) { EACCES, EPERM); } + +/* Three difference errors for "not enough disk space" */ +static inline bool ERRNO_IS_DISK_SPACE(int r) { + return IN_SET(abs(r), + ENOSPC, + EDQUOT, + EFBIG); +} From 282bde106652057c5d278a13dd3a0044ca5a9765 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 8 Aug 2019 19:53:17 +0200 Subject: [PATCH 04/11] memory-util: introduce erase_and_free() helper --- src/basic/memory-util.h | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/basic/memory-util.h b/src/basic/memory-util.h index 46a6907a0c..4f92a6434a 100644 --- a/src/basic/memory-util.h +++ b/src/basic/memory-util.h @@ -80,14 +80,21 @@ static inline void* explicit_bzero_safe(void *p, size_t l) { void *explicit_bzero_safe(void *p, size_t l); #endif -static inline void erase_and_freep(void *p) { - void *ptr = *(void**) p; +static inline void* erase_and_free(void *p) { + size_t l; - if (ptr) { - size_t l = malloc_usable_size(ptr); - explicit_bzero_safe(ptr, l); - free(ptr); - } + if (!p) + return NULL; + + l = malloc_usable_size(p); + explicit_bzero_safe(p, l); + free(p); + + return NULL; +} + +static inline void erase_and_freep(void *p) { + erase_and_free(*(void**) p); } /* Use with _cleanup_ to erase a single 'char' when leaving scope */ From 53caaffdf4a6b64c52f1215577c48835c2f9c9a0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 11 Jul 2019 14:50:26 +0200 Subject: [PATCH 05/11] string-util: readd string_erase() This was dropped in 8e27167cc9b8beda2bf49789b15f0b0301b95d17, but is actually useful for some usecases still. --- src/basic/string-util.c | 10 ++++++++++ src/basic/string-util.h | 2 ++ src/test/test-string-util.c | 24 ++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 8e6aa63806..b477a51534 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -1064,3 +1064,13 @@ bool string_is_safe(const char *p) { return true; } + +char* string_erase(char *x) { + if (!x) + return NULL; + + /* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we + * used them. */ + explicit_bzero_safe(x, strlen(x)); + return x; +} diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 04cc82b386..f10af9ad2f 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -278,3 +278,5 @@ static inline char* str_realloc(char **p) { return (*p = t); } + +char* string_erase(char *x); diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 8ea3994366..7a05afb4ac 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -9,6 +9,29 @@ #include "utf8.h" #include "util.h" +static void test_string_erase(void) { + char *x; + + x = strdupa(""); + assert_se(streq(string_erase(x), "")); + + x = strdupa("1"); + assert_se(streq(string_erase(x), "")); + + x = strdupa("123456789"); + assert_se(streq(string_erase(x), "")); + + assert_se(x[1] == '\0'); + assert_se(x[2] == '\0'); + assert_se(x[3] == '\0'); + assert_se(x[4] == '\0'); + assert_se(x[5] == '\0'); + assert_se(x[6] == '\0'); + assert_se(x[7] == '\0'); + assert_se(x[8] == '\0'); + assert_se(x[9] == '\0'); +} + static void test_free_and_strndup_one(char **t, const char *src, size_t l, const char *expected, bool change) { int r; @@ -543,6 +566,7 @@ static void test_memory_startswith_no_case(void) { int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); + test_string_erase(); test_free_and_strndup(); test_ascii_strcasecmp_n(); test_ascii_strcasecmp_nn(); From 6093b2bb05f314b7f8bcf50b2a55d8e0713e96d0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 Aug 2019 12:34:29 +0200 Subject: [PATCH 06/11] user-util: export is_nologin_shell() so that we can use it elsewhere --- src/basic/user-util.c | 2 +- src/basic/user-util.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 93ba4eedd2..985a669a1b 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -84,7 +84,7 @@ char *getusername_malloc(void) { return uid_to_name(getuid()); } -static bool is_nologin_shell(const char *shell) { +bool is_nologin_shell(const char *shell) { return PATH_IN_SET(shell, /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice diff --git a/src/basic/user-util.h b/src/basic/user-util.h index cfa515f5e8..d507c135eb 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -127,3 +127,5 @@ int putsgent_sane(const struct sgrp *sg, FILE *stream); #endif int make_salt(char **ret); + +bool is_nologin_shell(const char *shell); From a3292ec8d7049d1e78ab47b390cd8098e26c889f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 Aug 2019 12:34:46 +0200 Subject: [PATCH 07/11] user-util: add uid_is_container() for checking whether UID is in container range We have similar calls for the dynamic user and system range, let's add this too here. --- src/basic/user-util.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/basic/user-util.h b/src/basic/user-util.h index d507c135eb..7488071086 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -57,6 +57,14 @@ int take_etc_passwd_lock(const char *root); #define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock" +static inline bool uid_is_system(uid_t uid) { + return uid <= SYSTEM_UID_MAX; +} + +static inline bool gid_is_system(gid_t gid) { + return gid <= SYSTEM_GID_MAX; +} + static inline bool uid_is_dynamic(uid_t uid) { return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX; } @@ -65,12 +73,12 @@ static inline bool gid_is_dynamic(gid_t gid) { return uid_is_dynamic((uid_t) gid); } -static inline bool uid_is_system(uid_t uid) { - return uid <= SYSTEM_UID_MAX; +static inline bool uid_is_container(uid_t uid) { + return CONTAINER_UID_BASE_MIN <= uid && uid <= CONTAINER_UID_BASE_MAX; } -static inline bool gid_is_system(gid_t gid) { - return gid <= SYSTEM_GID_MAX; +static inline bool gid_is_container(gid_t gid) { + return uid_is_container((uid_t) gid); } /* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer From e5ea9ed030788de9e6be108dfc278ff14f3ddf30 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Apr 2019 15:23:48 +0200 Subject: [PATCH 08/11] tmpfile-util: if no path is passed to fopen_temporary() make one up Let's beef up functionality a bit, and modernize the whole function. --- src/basic/tmpfile-util.c | 46 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c index 7cdaca4e40..89fae1c453 100644 --- a/src/basic/tmpfile-util.c +++ b/src/basic/tmpfile-util.c @@ -19,38 +19,48 @@ #include "tmpfile-util.h" #include "umask-util.h" -int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { - FILE *f; - char *t; - int r, fd; +int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *t = NULL; + _cleanup_close_ int fd = -1; + int r; - assert(path); - assert(_f); - assert(_temp_path); + if (path) { + r = tempfn_xxxxxx(path, NULL, &t); + if (r < 0) + return r; + } else { + const char *d; - r = tempfn_xxxxxx(path, NULL, &t); - if (r < 0) - return r; + r = tmp_dir(&d); + if (r < 0) + return r; + + t = path_join(d, "XXXXXX"); + if (!t) + return -ENOMEM; + } fd = mkostemp_safe(t); - if (fd < 0) { - free(t); + if (fd < 0) return -errno; - } /* This assumes that returned FILE object is short-lived and used within the same single-threaded * context and never shared externally, hence locking is not necessary. */ r = fdopen_unlocked(fd, "w", &f); if (r < 0) { - unlink(t); - free(t); - safe_close(fd); + (void) unlink(t); return r; } - *_f = f; - *_temp_path = t; + TAKE_FD(fd); + + if (ret_f) + *ret_f = TAKE_PTR(f); + + if (ret_temp_path) + *ret_temp_path = TAKE_PTR(t); return 0; } From 7a509acc297ae39ae4d54637953e598c72ad8036 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Jul 2019 16:48:32 +0200 Subject: [PATCH 09/11] tmpfile-util: modernize mkostemp_safe() a bit --- src/basic/tmpfile-util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c index 89fae1c453..2d201e1a57 100644 --- a/src/basic/tmpfile-util.c +++ b/src/basic/tmpfile-util.c @@ -67,12 +67,12 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) { /* This is much like mkostemp() but is subject to umask(). */ int mkostemp_safe(char *pattern) { - _unused_ _cleanup_umask_ mode_t u = umask(0077); int fd; assert(pattern); - fd = mkostemp(pattern, O_CLOEXEC); + RUN_WITH_UMASK(0077) + fd = mkostemp(pattern, O_CLOEXEC); if (fd < 0) return -errno; From 8987afc4d12cce4f8b89c6d963aec8f44bf3cc45 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 6 May 2019 22:38:43 +0200 Subject: [PATCH 10/11] process-util: add new safe_fork() flag for connecting stdout to stderr This adds a new safe_fork() flag. If set the child process' fd 1 becomes fd 2 of the caller. This is useful for invoking tools (such as various mkfs/fsck implementations) that output status messages to stdout, but which we invoke and don't want to pollute stdout with their output. --- src/basic/process-util.c | 7 +++++++ src/basic/process-util.h | 21 +++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 9b6c4c31f7..f996614bb6 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1337,6 +1337,13 @@ int safe_fork_full( log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); _exit(EXIT_FAILURE); } + + } else if (flags & FORK_STDOUT_TO_STDERR) { + + if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) { + log_full_errno(prio, r, "Failed to connect stdout to stderr: %m"); + _exit(EXIT_FAILURE); + } } if (flags & FORK_RLIMIT_NOFILE_SAFE) { diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 5f4e174f04..8f5addd4b9 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -147,16 +147,17 @@ void reset_cached_pid(void); int must_be_root(void); typedef enum ForkFlags { - FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */ - FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */ - FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */ - FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */ - FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */ - FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */ - FORK_WAIT = 1 << 6, /* Wait until child exited */ - FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */ - FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */ - FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ + FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */ + FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */ + FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */ + FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */ + FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */ + FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */ + FORK_WAIT = 1 << 6, /* Wait until child exited */ + FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */ + FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */ + FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ + FORK_STDOUT_TO_STDERR = 1 << 10, /* Make stdout a copy of stderr */ } ForkFlags; int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); From 6b636c2d27908d2a49b6af7139c85a073ce4ddd0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 May 2019 10:17:06 +0200 Subject: [PATCH 11/11] main-func: send main exit code to parent via sd_notify() on exit So far we silently convert negative return values from run() as EXIT_FAILURE, which is how UNIX expects it. In many cases it would be very useful for the caller to retrieve the actual error number we exit with. Let's generically return that via sd_notify()'s ERRNO= attribute. This means callers can set $NOTIFY_SOCKET and get the actual error number delivered at their doorstep just like that. --- src/shared/main-func.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/shared/main-func.h b/src/shared/main-func.h index 6c26cb9fb5..cf23ad450c 100644 --- a/src/shared/main-func.h +++ b/src/shared/main-func.h @@ -3,6 +3,8 @@ #include +#include "sd-daemon.h" + #include "pager.h" #include "selinux-util.h" #include "spawn-ask-password-agent.h" @@ -16,6 +18,8 @@ save_argc_argv(argc, argv); \ intro; \ r = impl; \ + if (r < 0) \ + (void) sd_notifyf(0, "ERRNO=%i", -r); \ ask_password_agent_close(); \ polkit_agent_close(); \ pager_close(); \