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); +} 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 */ 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); } 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; } diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 718a237954..d60d8275ca 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1338,6 +1338,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 33dc871229..57582738b3 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); 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/basic/tmpfile-util.c b/src/basic/tmpfile-util.c index 7cdaca4e40..2d201e1a57 100644 --- a/src/basic/tmpfile-util.c +++ b/src/basic/tmpfile-util.c @@ -19,50 +19,60 @@ #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; } /* 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; 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..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 @@ -127,3 +135,5 @@ int putsgent_sane(const struct sgrp *sg, FILE *stream); #endif int make_salt(char **ret); + +bool is_nologin_shell(const char *shell); 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(); \ 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();