diff options
author | Thomas Haller <thaller@redhat.com> | 2018-10-28 08:59:02 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-10-28 09:31:31 +0100 |
commit | 3648c58bc5f0e5195ad946b8a3e5efe65c89e4c0 (patch) | |
tree | b7726e07e32f8fa623e07aef47c42ab50b99db56 | |
parent | ef7312a3ae3527e68738b2a7325aaae969fc7355 (diff) | |
parent | 5437448a6404376f2e8fdadff329682bfe9c992e (diff) | |
download | NetworkManager-3648c58bc5f0e5195ad946b8a3e5efe65c89e4c0.tar.gz |
systemd: merge branch systemd into master
40 files changed, 572 insertions, 459 deletions
diff --git a/Makefile.am b/Makefile.am index a123d1fa1f..a57dbe4438 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1483,6 +1483,7 @@ src_libsystemd_nm_la_SOURCES = \ src/systemd/sd-adapt/raw-clone.h \ src/systemd/sd-adapt/sd-daemon.h \ src/systemd/sd-adapt/sd-device.h \ + src/systemd/sd-adapt/serialize.h \ src/systemd/sd-adapt/stat-util.h \ src/systemd/sd-adapt/terminal-util.h \ src/systemd/sd-adapt/unaligned.h \ diff --git a/src/systemd/nm-sd.c b/src/systemd/nm-sd.c index cbef91d1fe..44867ada4f 100644 --- a/src/systemd/nm-sd.c +++ b/src/systemd/nm-sd.c @@ -133,6 +133,10 @@ nm_sd_event_attach_default (void) /*****************************************************************************/ +const bool mempool_use_allowed = true; + +/*****************************************************************************/ + /* ensure that defines in nm-sd.h correspond to the internal defines. */ #include "nm-sd-adapt.h" diff --git a/src/systemd/sd-adapt/serialize.h b/src/systemd/sd-adapt/serialize.h new file mode 100644 index 0000000000..637892c2d6 --- /dev/null +++ b/src/systemd/sd-adapt/serialize.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h index ebe42889ea..2a6deb12ca 100644 --- a/src/systemd/src/basic/alloc-util.h +++ b/src/systemd/src/basic/alloc-util.h @@ -46,6 +46,21 @@ static inline void *mfree(void *memory) { void* memdup(const void *p, size_t l) _alloc_(2); void* memdup_suffix0(const void *p, size_t l) _alloc_(2); +#define memdupa(p, l) \ + ({ \ + void *_q_; \ + _q_ = alloca(l); \ + memcpy(_q_, p, l); \ + }) + +#define memdupa_suffix0(p, l) \ + ({ \ + void *_q_; \ + _q_ = alloca(l + 1); \ + ((uint8_t*) _q_)[l] = 0; \ + memcpy(_q_, p, l); \ + }) + static inline void freep(void *p) { free(*(void**) p); } diff --git a/src/systemd/src/basic/env-util.c b/src/systemd/src/basic/env-util.c index 36f3c2b7d7..1cd48a27e9 100644 --- a/src/systemd/src/basic/env-util.c +++ b/src/systemd/src/basic/env-util.c @@ -24,10 +24,6 @@ DIGITS LETTERS \ "_" -#ifndef ARG_MAX -#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX)) -#endif - static bool env_name_is_valid_n(const char *e, size_t n) { const char *p; @@ -45,7 +41,7 @@ static bool env_name_is_valid_n(const char *e, size_t n) { * either. Discounting the equal sign and trailing NUL this * hence leaves ARG_MAX-2 as longest possible variable * name. */ - if (n > ARG_MAX - 2) + if (n > (size_t) sysconf(_SC_ARG_MAX) - 2) return false; for (p = e; p < e + n; p++) @@ -79,7 +75,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) > ARG_MAX - 3) + if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3) return false; return true; @@ -102,7 +98,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) > ARG_MAX - 1) + if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1) return false; return true; @@ -128,30 +124,28 @@ bool strv_env_is_valid(char **e) { } bool strv_env_name_is_valid(char **l) { - char **p, **q; + char **p; STRV_FOREACH(p, l) { if (!env_name_is_valid(*p)) return false; - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; + if (strv_contains(p + 1, *p)) + return false; } return true; } bool strv_env_name_or_assignment_is_valid(char **l) { - char **p, **q; + char **p; STRV_FOREACH(p, l) { if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) return false; - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; + if (strv_contains(p + 1, *p)) + return false; } return true; @@ -160,20 +154,23 @@ bool strv_env_name_or_assignment_is_valid(char **l) { static int env_append(char **r, char ***k, char **a) { assert(r); assert(k); + assert(*k >= r); if (!a) return 0; - /* Add the entries of a to *k unless they already exist in *r - * in which case they are overridden instead. This assumes - * there is enough space in the r array. */ + /* Expects the following arguments: 'r' shall point to the beginning of an strv we are going to append to, 'k' + * to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv. + * + * This call adds every entry of 'a' to 'r', either overriding an existing matching entry, or appending to it. + * + * This call assumes 'r' has enough pre-allocated space to grow by all of 'a''s items. */ for (; *a; a++) { - char **j; + char **j, *c; size_t n; n = strcspn(*a, "="); - if ((*a)[n] == '=') n++; @@ -181,24 +178,26 @@ static int env_append(char **r, char ***k, char **a) { if (strneq(*j, *a, n)) break; - if (j >= *k) - (*k)++; - else - free(*j); - - *j = strdup(*a); - if (!*j) + c = strdup(*a); + if (!c) return -ENOMEM; + + if (j >= *k) { /* Append to the end? */ + (*k)[0] = c; + (*k)[1] = NULL; + (*k)++; + } else + free_and_replace(*j, c); /* Override existing item */ } return 0; } char **strv_env_merge(size_t n_lists, ...) { - size_t n = 0; - char **l, **k, **r; + _cleanup_strv_free_ char **ret = NULL; + size_t n = 0, i; + char **l, **k; va_list ap; - size_t i; /* Merges an arbitrary number of environment sets */ @@ -209,29 +208,24 @@ char **strv_env_merge(size_t n_lists, ...) { } va_end(ap); - r = new(char*, n+1); - if (!r) + ret = new(char*, n+1); + if (!ret) return NULL; - k = r; + *ret = NULL; + k = ret; va_start(ap, n_lists); for (i = 0; i < n_lists; i++) { l = va_arg(ap, char**); - if (env_append(r, &k, l) < 0) - goto fail; + if (env_append(ret, &k, l) < 0) { + va_end(ap); + return NULL; + } } va_end(ap); - *k = NULL; - - return r; - -fail: - va_end(ap); - strv_free(r); - - return NULL; + return TAKE_PTR(ret); } static bool env_match(const char *t, const char *pattern) { @@ -385,22 +379,23 @@ char **strv_env_unset_many(char **l, ...) { } int strv_env_replace(char ***l, char *p) { - char **f; const char *t, *name; + char **f; + int r; assert(p); - /* Replace first occurrence of the env var or add a new one in the - * string list. Drop other occurrences. Edits in-place. Does not copy p. - * p must be a valid key=value assignment. + /* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits + * in-place. Does not copy p. p must be a valid key=value assignment. */ t = strchr(p, '='); - assert(t); + if (!t) + return -EINVAL; name = strndupa(p, t - p); - for (f = *l; f && *f; f++) + STRV_FOREACH(f, *l) if (env_entry_has_name(*f, name)) { free_and_replace(*f, p); strv_env_unset(f + 1, *f); @@ -408,33 +403,40 @@ int strv_env_replace(char ***l, char *p) { } /* We didn't find a match, we need to append p or create a new strv */ - if (strv_push(l, p) < 0) - return -ENOMEM; + r = strv_push(l, p); + if (r < 0) + return r; + return 1; } char **strv_env_set(char **x, const char *p) { + _cleanup_strv_free_ char **ret = NULL; + size_t n, m; char **k; - _cleanup_strv_free_ char **r = NULL; - char* m[2] = { (char*) p, NULL }; /* Overrides the env var setting of p, returns a new copy */ - r = new(char*, strv_length(x)+2); - if (!r) + n = strv_length(x); + m = n + 2; + if (m < n) /* overflow? */ return NULL; - k = r; - if (env_append(r, &k, x) < 0) + ret = new(char*, m); + if (!ret) return NULL; - if (env_append(r, &k, m) < 0) + *ret = NULL; + k = ret; + + if (env_append(ret, &k, x) < 0) return NULL; - *k = NULL; + if (env_append(ret, &k, STRV_MAKE(p)) < 0) + return NULL; - return TAKE_PTR(r); + return TAKE_PTR(ret); } char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { @@ -755,37 +757,4 @@ int getenv_bool_secure(const char *p) { return parse_boolean(e); } - -int serialize_environment(FILE *f, char **environment) { - char **e; - - STRV_FOREACH(e, environment) { - _cleanup_free_ char *ce; - - ce = cescape(*e); - if (!ce) - return -ENOMEM; - - fprintf(f, "env=%s\n", ce); - } - - /* caller should call ferror() */ - - return 0; -} - -int deserialize_environment(char ***environment, const char *line) { - char *uce; - int r; - - assert(line); - assert(environment); - - assert(startswith(line, "env=")); - r = cunescape(line + 4, 0, &uce); - if (r < 0) - return r; - - return strv_env_replace(environment, uce); -} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/env-util.h b/src/systemd/src/basic/env-util.h index 174433ea91..4d21ea6bef 100644 --- a/src/systemd/src/basic/env-util.h +++ b/src/systemd/src/basic/env-util.h @@ -45,6 +45,3 @@ char *strv_env_get(char **x, const char *n) _pure_; int getenv_bool(const char *p); int getenv_bool_secure(const char *p); - -int serialize_environment(FILE *f, char **environment); -int deserialize_environment(char ***environment, const char *line); diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c index 3913370a03..6c6068038a 100644 --- a/src/systemd/src/basic/fd-util.c +++ b/src/systemd/src/basic/fd-util.c @@ -357,22 +357,22 @@ bool fdname_is_valid(const char *s) { } int fd_get_path(int fd, char **ret) { - _cleanup_close_ int dir = -1; - char fdname[DECIMAL_STR_MAX(int)]; + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; int r; - dir = open("/proc/self/fd/", O_CLOEXEC | O_DIRECTORY | O_PATH); - if (dir < 0) - /* /proc is not available or not set up properly, we're most likely - * in some chroot environment. */ - return errno == ENOENT ? -EOPNOTSUPP : -errno; + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + r = readlink_malloc(procfs_path, ret); + if (r == -ENOENT) { + /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make + * things debuggable and distuingish the two. */ - xsprintf(fdname, "%i", fd); + if (access("/proc/self/fd/", F_OK) < 0) + /* /proc is not available or not set up properly, we're most likely in some chroot + * environment. */ + return errno == ENOENT ? -EOPNOTSUPP : -errno; - r = readlinkat_malloc(dir, fdname, ret); - if (r == -ENOENT) - /* If the file doesn't exist the fd is invalid */ - return -EBADF; + return -EBADF; /* The directory exists, hence it's the fd that doesn't. */ + } return r; } diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c index 24a1098605..b168ebf340 100644 --- a/src/systemd/src/basic/fileio.c +++ b/src/systemd/src/basic/fileio.c @@ -1231,6 +1231,24 @@ int mkostemp_safe(char *pattern) { return fd; } +int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { + int fd; + FILE *f; + + fd = mkostemp_safe(pattern); + if (fd < 0) + return fd; + + f = fdopen(fd, mode); + if (!f) { + safe_close(fd); + return -errno; + } + + *ret_f = f; + return 0; +} + int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { const char *fn; char *t; diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h index 77e6206e95..102d33d75f 100644 --- a/src/systemd/src/basic/fileio.h +++ b/src/systemd/src/basic/fileio.h @@ -59,20 +59,12 @@ DIR *xopendirat(int dirfd, const char *name, int flags); int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f); -#define FOREACH_LINE(line, f, on_error) \ - for (;;) \ - if (!fgets(line, sizeof(line), f)) { \ - if (ferror(f)) { \ - on_error; \ - } \ - break; \ - } else - int fflush_and_check(FILE *f); int fflush_sync_and_check(FILE *f); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); int mkostemp_safe(char *pattern); +int fmkostemp_safe(char *pattern, const char *mode, FILE**_f); int tempfn_xxxxxx(const char *p, const char *extra, char **ret); int tempfn_random(const char *p, const char *extra, char **ret); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c index 1433bb714c..5966c3835b 100644 --- a/src/systemd/src/basic/fs-util.c +++ b/src/systemd/src/basic/fs-util.c @@ -136,7 +136,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char #endif /* NM_IGNORED */ int readlinkat_malloc(int fd, const char *p, char **ret) { - size_t l = 100; + size_t l = FILENAME_MAX+1; int r; assert(p); @@ -1182,7 +1182,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { return 0; if (fstat(truncate_fd, &st) < 0) { - log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name); + log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name); return 0; } @@ -1244,6 +1244,34 @@ int fsync_directory_of_file(int fd) { return 0; } +int fsync_path_at(int at_fd, const char *path) { + _cleanup_close_ int opened_fd = -1; + int fd; + + if (isempty(path)) { + if (at_fd == AT_FDCWD) { + opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (opened_fd < 0) + return -errno; + + fd = opened_fd; + } else + fd = at_fd; + } else { + + opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC); + if (opened_fd < 0) + return -errno; + + fd = opened_fd; + } + + if (fsync(fd) < 0) + return -errno; + + return 0; +} + int open_parent(const char *path, int flags, mode_t mode) { _cleanup_free_ char *parent = NULL; int fd; diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h index bc753d5920..955b146a6a 100644 --- a/src/systemd/src/basic/fs-util.h +++ b/src/systemd/src/basic/fs-util.h @@ -105,5 +105,6 @@ void unlink_tempfilep(char (*p)[]); int unlinkat_deallocate(int fd, const char *name, int flags); int fsync_directory_of_file(int fd); +int fsync_path_at(int at_fd, const char *path); int open_parent(const char *path, int flags, mode_t mode); diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c index cfddeafe05..328c4d3a5a 100644 --- a/src/systemd/src/basic/hashmap.c +++ b/src/systemd/src/basic/hashmap.c @@ -8,7 +8,6 @@ #include <string.h> #include "alloc-util.h" -#include "env-util.h" #include "fileio.h" #include "hashmap.h" #include "macro.h" @@ -769,24 +768,12 @@ static void reset_direct_storage(HashmapBase *h) { memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); } -static bool use_pool(void) { - static int b = -1; - - if (!is_main_thread()) - return false; - - if (b < 0) - b = getenv_bool("SYSTEMD_MEMPOOL") != 0; - - return b; -} - static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { HashmapBase *h; const struct hashmap_type_info *hi = &hashmap_type_info[type]; bool up; - up = use_pool(); + up = mempool_enabled(); h = up ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); if (!h) @@ -1558,18 +1545,9 @@ static unsigned find_first_entry(HashmapBase *h) { return hashmap_iterate_entry(h, &i); } -void *internal_hashmap_first(HashmapBase *h) { - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - return entry_value(h, bucket_at(h, idx)); -} - -void *internal_hashmap_first_key(HashmapBase *h) { +void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { struct hashmap_base_entry *e; + void *key, *data; unsigned idx; idx = find_first_entry(h); @@ -1577,39 +1555,16 @@ void *internal_hashmap_first_key(HashmapBase *h) { return NULL; e = bucket_at(h, idx); - return (void*) e->key; -} - -void *internal_hashmap_steal_first(HashmapBase *h) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); + key = (void*) e->key; data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} -void *internal_hashmap_steal_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - void *key; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; + if (remove) + remove_entry(h, idx); - e = bucket_at(h, idx); - key = (void*) e->key; - remove_entry(h, idx); + if (ret_key) + *ret_key = key; - return key; + return data; } unsigned internal_hashmap_size(HashmapBase *h) { diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h index b771ceccdc..bb2a5c76ec 100644 --- a/src/systemd/src/basic/hashmap.h +++ b/src/systemd/src/basic/hashmap.h @@ -290,36 +290,51 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { * the first entry is O(1). */ -void *internal_hashmap_steal_first(HashmapBase *h); +void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); +static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); +} +static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); +} +static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); +} +static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); +} + + static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); +} +static inline void *hashmap_first(Hashmap *h) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); +} +static inline void *ordered_hashmap_first(OrderedHashmap *h) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } -void *internal_hashmap_steal_first_key(HashmapBase *h); +static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) { + void *key = NULL; + + (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); + return key; +} static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); + return internal_hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); + return internal_hashmap_first_key(HASHMAP_BASE(h), true); } - -void *internal_hashmap_first_key(HashmapBase *h) _pure_; static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); + return internal_hashmap_first_key(HASHMAP_BASE(h), false); } static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first(HashmapBase *h) _pure_; -static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key(HASHMAP_BASE(h), false); } #define hashmap_clear_with_destructor(_s, _f) \ diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h index 643e0bea88..040680c30a 100644 --- a/src/systemd/src/basic/list.h +++ b/src/systemd/src/basic/list.h @@ -38,9 +38,9 @@ /* Append an item to the list */ #define LIST_APPEND(name,head,item) \ do { \ - typeof(*(head)) *_tail; \ - LIST_FIND_TAIL(name,head,_tail); \ - LIST_INSERT_AFTER(name,head,_tail,item); \ + typeof(*(head)) **_hhead = &(head), *_tail; \ + LIST_FIND_TAIL(name, *_hhead, _tail); \ + LIST_INSERT_AFTER(name, *_hhead, _tail, item); \ } while (false) /* Remove an item from the list */ diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h index 9f8bd8abf8..fdb575f447 100644 --- a/src/systemd/src/basic/macro.h +++ b/src/systemd/src/basic/macro.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <assert.h> #include <inttypes.h> #include <stdbool.h> #include <sys/param.h> @@ -57,10 +58,6 @@ #if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__) /* Temporarily disable some warnings */ -#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") - #define DISABLE_WARNING_FORMAT_NONLITERAL \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") @@ -323,20 +320,13 @@ static inline int __coverity_check__(int condition) { } while (false) #if defined(static_assert) -/* static_assert() is sometimes defined in a way that trips up - * -Wdeclaration-after-statement, hence let's temporarily turn off - * this warning around it. */ #define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - static_assert(expr, #expr); \ - REENABLE_WARNING + static_assert(expr, #expr); #else #define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ struct CONCATENATE(_assert_struct_, __COUNTER__) { \ char x[(expr) ? 0 : -1]; \ - }; \ - REENABLE_WARNING + }; #endif #define assert_return(expr, r) \ @@ -355,7 +345,7 @@ static inline int __coverity_check__(int condition) { #define PTR_TO_INT(p) ((int) ((intptr_t) (p))) #define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) +#define PTR_TO_UINT(p) ((unsigned) ((uintptr_t) (p))) #define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) #define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) @@ -435,8 +425,11 @@ static inline int __coverity_check__(int condition) { #define IN_SET(x, ...) \ ({ \ bool _found = false; \ - /* If the build breaks in the line below, you need to extend the case macros */ \ - static _unused_ char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \ + /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ + * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ + * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ + * doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \ + assert_cc((sizeof((long double[]){__VA_ARGS__})/sizeof(long double)) <= 20); \ switch(x) { \ FOR_EACH_MAKE_CASE(__VA_ARGS__) \ _found = true; \ diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c index dd1b1e5ffa..bae4f2fcd6 100644 --- a/src/systemd/src/basic/mempool.c +++ b/src/systemd/src/basic/mempool.c @@ -5,8 +5,10 @@ #include <stdint.h> #include <stdlib.h> +#include "env-util.h" #include "macro.h" #include "mempool.h" +#include "process-util.h" #include "util.h" struct pool { @@ -72,8 +74,21 @@ void mempool_free_tile(struct mempool *mp, void *p) { mp->freelist = p; } -#if VALGRIND +bool mempool_enabled(void) { + static int b = -1; + + if (!is_main_thread()) + return false; + if (!mempool_use_allowed) + b = false; + if (b < 0) + b = getenv_bool("SYSTEMD_MEMPOOL") != 0; + + return b; +} + +#if VALGRIND void mempool_drop(struct mempool *mp) { struct pool *p = mp->first_pool; while (p) { @@ -83,5 +98,4 @@ void mempool_drop(struct mempool *mp) { p = n; } } - #endif diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h index 4098535c6f..0eecca0f92 100644 --- a/src/systemd/src/basic/mempool.h +++ b/src/systemd/src/basic/mempool.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <stdbool.h> #include <stddef.h> struct pool; @@ -22,6 +23,9 @@ static struct mempool pool_name = { \ .at_least = alloc_at_least, \ } +extern const bool mempool_use_allowed; +bool mempool_enabled(void); + #if VALGRIND void mempool_drop(struct mempool *mp); #endif diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c index 5877957822..dc37e04dd7 100644 --- a/src/systemd/src/basic/path-util.c +++ b/src/systemd/src/basic/path-util.c @@ -344,7 +344,7 @@ char *path_simplify(char *path, bool kill_dots) { */ if (isempty(path)) - return path; + return path; absolute = path_is_absolute(path); @@ -785,24 +785,32 @@ bool filename_is_valid(const char *p) { if (*e != 0) return false; - if (e - p > FILENAME_MAX) + if (e - p > FILENAME_MAX) /* FILENAME_MAX is counted *without* the trailing NUL byte */ return false; return true; } -bool path_is_normalized(const char *p) { +bool path_is_valid(const char *p) { if (isempty(p)) return false; - if (dot_or_dot_dot(p)) + if (strlen(p) >= PATH_MAX) /* PATH_MAX is counted *with* the trailing NUL byte */ return false; - if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) + return true; +} + +bool path_is_normalized(const char *p) { + + if (!path_is_valid(p)) + return false; + + if (dot_or_dot_dot(p)) return false; - if (strlen(p)+1 > PATH_MAX) + if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) return false; if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h index 72f3ce3683..4a4bd4cd51 100644 --- a/src/systemd/src/basic/path-util.h +++ b/src/systemd/src/basic/path-util.h @@ -136,6 +136,7 @@ char* dirname_malloc(const char *path); const char *last_path_component(const char *path); bool filename_is_valid(const char *p) _pure_; +bool path_is_valid(const char *p) _pure_; bool path_is_normalized(const char *p) _pure_; char *file_in_same_dir(const char *path, const char *filename); diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c index 79900df353..3af3402504 100644 --- a/src/systemd/src/basic/prioq.c +++ b/src/systemd/src/basic/prioq.c @@ -34,11 +34,14 @@ struct Prioq { Prioq *prioq_new(compare_func_t compare_func) { Prioq *q; - q = new0(Prioq, 1); + q = new(Prioq, 1); if (!q) return q; - q->compare_func = compare_func; + *q = (Prioq) { + .compare_func = compare_func, + }; + return q; } @@ -90,6 +93,7 @@ static void swap(Prioq *q, unsigned j, unsigned k) { static unsigned shuffle_up(Prioq *q, unsigned idx) { assert(q); + assert(idx < q->n_items); while (idx > 0) { unsigned k; @@ -213,9 +217,12 @@ _pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) assert(q); + if (q->n_items <= 0) + return NULL; + if (idx) { if (*idx == PRIOQ_IDX_NULL || - *idx > q->n_items) + *idx >= q->n_items) return NULL; i = q->items + *idx; diff --git a/src/systemd/src/basic/prioq.h b/src/systemd/src/basic/prioq.h index e036175260..bba5c7caa4 100644 --- a/src/systemd/src/basic/prioq.h +++ b/src/systemd/src/basic/prioq.h @@ -12,6 +12,7 @@ typedef struct Prioq Prioq; Prioq *prioq_new(compare_func_t compare); Prioq *prioq_free(Prioq *q); +DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free); int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); int prioq_put(Prioq *q, void *data, unsigned *idx); diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c index 71d737a373..b5f0aafd0a 100644 --- a/src/systemd/src/basic/process-util.c +++ b/src/systemd/src/basic/process-util.c @@ -29,6 +29,7 @@ #include "alloc-util.h" #include "architecture.h" +#include "def.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" @@ -341,15 +342,33 @@ int rename_process(const char name[]) { /* Now, let's tell the kernel about this new memory */ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m"); - (void) munmap(nn, nn_size); - goto use_saved_argv; - } + /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be + * below the desired start address, in which case the kernel may have kicked this back due + * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in + * action). The proper solution would be to have a prctl() API that could set both start+end + * simultaneously, or at least let us query the existing address to anticipate this condition + * and respond accordingly. For now, we can only guess at the cause of this failure and try + * a workaround--which will briefly expand the arg space to something potentially huge before + * resizing it to what we want. */ + log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m"); + + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) { + log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); + (void) munmap(nn, nn_size); + goto use_saved_argv; + } - /* And update the end pointer to the new end, too. If this fails, we don't really know what to do, it's - * pretty unlikely that we can rollback, hence we'll just accept the failure, and continue. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { + log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); + goto use_saved_argv; + } + } else { + /* And update the end pointer to the new end, too. If this fails, we don't really know what + * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure, + * and continue. */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) + log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + } if (mm) (void) munmap(mm, mm_size); @@ -501,8 +520,8 @@ int get_process_exe(pid_t pid, char **name) { static int get_process_id(pid_t pid, const char *field, uid_t *uid) { _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; const char *p; + int r; assert(field); assert(uid); @@ -520,9 +539,16 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) { (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - FOREACH_LINE(line, f, return -errno) { + for (;;) { + _cleanup_free_ char *line = NULL; char *l; + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + l = strstrip(line); if (startswith(l, field)) { diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h index b16697711c..c7aa45f84f 100644 --- a/src/systemd/src/basic/process-util.h +++ b/src/systemd/src/basic/process-util.h @@ -134,13 +134,6 @@ static inline bool ioprio_priority_is_valid(int i) { static inline bool pid_is_valid(pid_t p) { return p > 0; } - -static inline int sched_policy_to_string_alloc_with_check(int n, char **s) { - if (!sched_policy_is_valid(n)) - return -EINVAL; - - return sched_policy_to_string_alloc(n, s); -} #endif /* NM_IGNORED */ int ioprio_parse_priority(const char *s, int *ret); @@ -168,7 +161,7 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { return safe_fork_full(name, NULL, 0, flags, ret_pid); } -int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...); +int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...) _sentinel_; int set_oom_score_adjust(int value); diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h index 664713810d..0d99d5550d 100644 --- a/src/systemd/src/basic/set.h +++ b/src/systemd/src/basic/set.h @@ -86,7 +86,7 @@ static inline void set_clear_free(Set *s) { /* no set_clear_free_free */ static inline void *set_steal_first(Set *s) { - return internal_hashmap_steal_first(HASHMAP_BASE(s)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); } #define set_clear_with_destructor(_s, _f) \ @@ -105,7 +105,7 @@ static inline void *set_steal_first(Set *s) { /* no set_first_key */ static inline void *set_first(Set *s) { - return internal_hashmap_first(HASHMAP_BASE(s)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), false, NULL); } /* no set_next */ diff --git a/src/systemd/src/basic/siphash24.h b/src/systemd/src/basic/siphash24.h index 77bb9a8c07..42a4ec0a49 100644 --- a/src/systemd/src/basic/siphash24.h +++ b/src/systemd/src/basic/siphash24.h @@ -3,6 +3,7 @@ #include <inttypes.h> #include <stddef.h> #include <stdint.h> +#include <string.h> #include <sys/types.h> #if 0 /* NM_IGNORED */ @@ -51,3 +52,7 @@ void siphash24_compress(const void *in, size_t inlen, struct siphash *state); uint64_t siphash24_finalize(struct siphash *state); uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); + +static inline uint64_t siphash24_string(const char *s, const uint8_t k[16]) { + return siphash24(s, strlen(s) + 1, k); +} diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c index 2e3c82705d..4476f3dc7a 100644 --- a/src/systemd/src/basic/socket-util.c +++ b/src/systemd/src/basic/socket-util.c @@ -60,8 +60,9 @@ int socket_address_parse(SocketAddress *a, const char *s) { assert(a); assert(s); - zero(*a); - a->type = SOCK_STREAM; + *a = (SocketAddress) { + .type = SOCK_STREAM, + }; if (*s == '[') { uint16_t port; @@ -99,7 +100,9 @@ int socket_address_parse(SocketAddress *a, const char *s) { size_t l; l = strlen(s); - if (l >= sizeof(a->sockaddr.un.sun_path)) + if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when + * parsing (the kernel itself is less strict here in what it + * accepts) */ return -EINVAL; a->sockaddr.un.sun_family = AF_UNIX; @@ -111,7 +114,11 @@ int socket_address_parse(SocketAddress *a, const char *s) { size_t l; l = strlen(s+1); - if (l >= sizeof(a->sockaddr.un.sun_path) - 1) + if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminate sockets here + * when parsing, even though abstract namespace sockets + * explicitly allow embedded NUL bytes and don't consider + * them special. But it's simply annoying to debug such + * sockets. */ return -EINVAL; a->sockaddr.un.sun_family = AF_UNIX; @@ -289,19 +296,28 @@ int socket_address_verify(const SocketAddress *a) { case AF_UNIX: if (a->size < offsetof(struct sockaddr_un, sun_path)) return -EINVAL; + if (a->size > sizeof(struct sockaddr_un)+1) /* Allow one extra byte, since getsockname() on Linux will + * append a NUL byte if we have path sockets that are above + * sun_path' full size */ + return -EINVAL; - if (a->size > offsetof(struct sockaddr_un, sun_path)) { - - if (a->sockaddr.un.sun_path[0] != 0) { - char *e; + if (a->size > offsetof(struct sockaddr_un, sun_path) && + a->sockaddr.un.sun_path[0] != 0) { /* Only validate file system sockets here */ - /* path */ - e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); - if (!e) - return -EINVAL; + const char *e; + e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); + if (e) { + /* If there's an embedded NUL byte, make sure the size of the socket addresses matches it */ if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) return -EINVAL; + } else { + /* If there's no embedded NUL byte, then then the size needs to match the whole + * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful, + * and considers both equivalent: getsockname() even extends sockaddr_un beyond its + * size if the path is non NUL terminated.)*/ + if (!IN_SET(a->size, sizeof(a->sockaddr.un.sun_path), sizeof(a->sockaddr.un.sun_path)+1)) + return -EINVAL; } } @@ -485,6 +501,10 @@ const char* socket_address_get_path(const SocketAddress *a) { if (a->sockaddr.un.sun_path[0] == 0) return NULL; + /* Note that this is only safe because we know that there's an extra NUL byte after the sockaddr_un + * structure. On Linux AF_UNIX file system socket addresses don't have to be NUL terminated if they take up the + * full sun_path space. */ + assert_cc(sizeof(union sockaddr_union) >= sizeof(struct sockaddr_un)+1); return a->sockaddr.un.sun_path; } @@ -750,21 +770,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) return 0; } -int socket_address_unlink(SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return 0; - - if (a->sockaddr.un.sun_path[0] == 0) - return 0; - - if (unlink(a->sockaddr.un.sun_path) < 0) - return -errno; - - return 1; -} - static const char* const netlink_family_table[] = { [NETLINK_ROUTE] = "route", [NETLINK_FIREWALL] = "firewall", @@ -837,10 +842,11 @@ int fd_inc_sndbuf(int fd, size_t n) { /* If we have the privileges we will ignore the kernel limit. */ - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) - return -errno; + if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n) < 0) { + r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n); + if (r < 0) + return r; + } return 1; } @@ -855,10 +861,12 @@ int fd_inc_rcvbuf(int fd, size_t n) { /* If we have the privileges we will ignore the kernel limit. */ - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) - return -errno; + if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n) < 0) { + r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n); + if (r < 0) + return r; + } + return 1; } @@ -1251,4 +1259,72 @@ int socket_ioctl_fd(void) { return fd; } + +int sockaddr_un_unlink(const struct sockaddr_un *sa) { + const char *p, * nul; + + assert(sa); + + if (sa->sun_family != AF_UNIX) + return -EPROTOTYPE; + + if (sa->sun_path[0] == 0) /* Nothing to do for abstract sockets */ + return 0; + + /* The path in .sun_path is not necessarily NUL terminated. Let's fix that. */ + nul = memchr(sa->sun_path, 0, sizeof(sa->sun_path)); + if (nul) + p = sa->sun_path; + else + p = memdupa_suffix0(sa->sun_path, sizeof(sa->sun_path)); + + if (unlink(p) < 0) + return -errno; + + return 1; +} + +int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { + size_t l; + + assert(ret); + assert(path); + + /* Initialize ret->sun_path from the specified argument. This will interpret paths starting with '@' as + * abstract namespace sockets, and those starting with '/' as regular filesystem sockets. It won't accept + * anything else (i.e. no relative paths), to avoid ambiguities. Note that this function cannot be used to + * reference paths in the abstract namespace that include NUL bytes in the name. */ + + l = strlen(path); + if (l == 0) + return -EINVAL; + if (!IN_SET(path[0], '/', '@')) + return -EINVAL; + if (path[1] == 0) + return -EINVAL; + + /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than + * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket + * addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that + * do not expect non-NUL terminated file system path*/ + if (l+1 > sizeof(ret->sun_path)) + return -EINVAL; + + *ret = (struct sockaddr_un) { + .sun_family = AF_UNIX, + }; + + if (path[0] == '@') { + /* Abstract namespace socket */ + memcpy(ret->sun_path + 1, path + 1, l); /* copy *with* trailing NUL byte */ + return (int) (offsetof(struct sockaddr_un, sun_path) + l); /* 🔥 *don't* 🔥 include trailing NUL in size */ + + } else { + assert(path[0] == '/'); + + /* File system socket */ + memcpy(ret->sun_path, path, l + 1); /* copy *with* trailing NUL byte */ + return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */ + } +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h index d7b814aefb..1ac30de5a3 100644 --- a/src/systemd/src/basic/socket-util.h +++ b/src/systemd/src/basic/socket-util.h @@ -73,7 +73,12 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s); int socket_address_parse_netlink(SocketAddress *a, const char *s); int socket_address_print(const SocketAddress *a, char **p); int socket_address_verify(const SocketAddress *a) _pure_; -int socket_address_unlink(SocketAddress *a); + +int sockaddr_un_unlink(const struct sockaddr_un *sa); + +static inline int socket_address_unlink(const SocketAddress *a) { + return socket_address_family(a) == AF_UNIX ? sockaddr_un_unlink(&a->sockaddr.un) : 0; +} bool socket_address_can_accept(const SocketAddress *a) _pure_; @@ -181,7 +186,16 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng offsetof(struct sockaddr_un, sun_path) + \ (_sa->sun_path[0] == 0 ? \ 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ - strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ + strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \ }) int socket_ioctl_fd(void); + +int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path); + +static inline int setsockopt_int(int fd, int level, int optname, int value) { + if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) + return -errno; + + return 0; +} diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c index a182555f39..1651239d8c 100644 --- a/src/systemd/src/basic/string-util.c +++ b/src/systemd/src/basic/string-util.c @@ -130,7 +130,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) { } /* Split a string into words. */ -const char* split(const char **state, size_t *l, const char *separator, bool quoted) { +const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) { const char *current; current = *state; @@ -146,20 +146,24 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo return NULL; } - if (quoted && strchr("\'\"", *current)) { + if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) { char quotechars[2] = {*current, '\0'}; *l = strcspn_escaped(current + 1, quotechars); if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || (current[*l + 2] && !strchr(separator, current[*l + 2]))) { /* right quote missing or garbage at the end */ + if (flags & SPLIT_RELAX) { + *state = current + *l + 1 + (current[*l + 1] != '\0'); + return current + 1; + } *state = current; return NULL; } *state = current++ + *l + 2; - } else if (quoted) { + } else if (flags & SPLIT_QUOTES) { *l = strcspn_escaped(current, separator); - if (current[*l] && !strchr(separator, current[*l])) { + if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) { /* unfinished escape */ *state = current; return NULL; @@ -398,12 +402,7 @@ int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) { if (r != 0) return r; - if (n < m) - return -1; - else if (n > m) - return 1; - else - return 0; + return CMP(n, m); } bool chars_intersect(const char *a, const char *b) { @@ -1068,8 +1067,11 @@ typedef void *(*memset_t)(void *,int,size_t); static volatile memset_t memset_func = memset; -void explicit_bzero(void *p, size_t l) { - memset_func(p, '\0', l); +void* explicit_bzero_safe(void *p, size_t l) { + if (l > 0) + memset_func(p, '\0', l); + + return p; } #endif @@ -1079,7 +1081,7 @@ char* string_erase(char *x) { /* A delicious drop of snake-oil! To be called on memory where * we stored passphrases or so, after we used them. */ - explicit_bzero(x, strlen(x)); + explicit_bzero_safe(x, strlen(x)); return x; } diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h index 72c075aa39..a5b5a16a5d 100644 --- a/src/systemd/src/basic/string-util.h +++ b/src/systemd/src/basic/string-util.h @@ -81,16 +81,21 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_; char *first_word(const char *s, const char *word) _pure_; -const char* split(const char **state, size_t *l, const char *separator, bool quoted); +typedef enum SplitFlags { + SPLIT_QUOTES = 0x01 << 0, + SPLIT_RELAX = 0x01 << 1, +} SplitFlags; + +const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags); #define FOREACH_WORD(word, length, s, state) \ - _FOREACH_WORD(word, length, s, WHITESPACE, false, state) + _FOREACH_WORD(word, length, s, WHITESPACE, 0, state) #define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ - _FOREACH_WORD(word, length, s, separator, false, state) + _FOREACH_WORD(word, length, s, separator, 0, state) -#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ - for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) +#define _FOREACH_WORD(word, length, s, separator, flags, state) \ + for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags))) char *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); @@ -193,8 +198,15 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const return memmem(haystack, haystacklen, needle, needlelen); } -#if !HAVE_EXPLICIT_BZERO -void explicit_bzero(void *p, size_t l); +#if HAVE_EXPLICIT_BZERO +static inline void* explicit_bzero_safe(void *p, size_t l) { + if (l > 0) + explicit_bzero(p, l); + + return p; +} +#else +void *explicit_bzero_safe(void *p, size_t l); #endif char *string_erase(char *x); diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c index 7951fa5022..992e448567 100644 --- a/src/systemd/src/basic/strv.c +++ b/src/systemd/src/basic/strv.c @@ -247,7 +247,7 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { return 0; } -char **strv_split(const char *s, const char *separator) { +char **strv_split_full(const char *s, const char *separator, SplitFlags flags) { const char *word, *state; size_t l; size_t n, i; @@ -255,12 +255,15 @@ char **strv_split(const char *s, const char *separator) { assert(s); + if (!separator) + separator = WHITESPACE; + s += strspn(s, separator); if (isempty(s)) return new0(char*, 1); n = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) + _FOREACH_WORD(word, l, s, separator, flags, state) n++; r = new(char*, n+1); @@ -268,7 +271,7 @@ char **strv_split(const char *s, const char *separator) { return NULL; i = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { + _FOREACH_WORD(word, l, s, separator, flags, state) { r[i] = strndup(word, l); if (!r[i]) { strv_free(r); diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h index 34a660cb92..e9e6063f58 100644 --- a/src/systemd/src/basic/strv.h +++ b/src/systemd/src/basic/strv.h @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "extract-word.h" #include "macro.h" +#include "string-util.h" #include "util.h" char *strv_find(char **l, const char *name) _pure_; @@ -66,7 +67,10 @@ static inline bool strv_isempty(char * const *l) { return !l || !*l; } -char **strv_split(const char *s, const char *separator); +char **strv_split_full(const char *s, const char *separator, SplitFlags flags); +static inline char **strv_split(const char *s, const char *separator) { + return strv_split_full(s, separator, 0); +} char **strv_split_newlines(const char *s); int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c index 5bceac6b9c..31417d8d51 100644 --- a/src/systemd/src/basic/time-util.c +++ b/src/systemd/src/basic/time-util.c @@ -16,6 +16,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "def.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -25,6 +26,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "serialize.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -282,7 +284,7 @@ static char *format_timestamp_internal( /* Let's not format times with years > 9999 */ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { - assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1); + assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1); strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); return buf; } @@ -534,64 +536,6 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { } #if 0 /* NM_IGNORED */ -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { - - assert(f); - assert(name); - assert(t); - - if (!dual_timestamp_is_set(t)) - return; - - fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n", - name, - t->realtime, - t->monotonic); -} - -int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { - uint64_t a, b; - int r, pos; - - assert(value); - assert(t); - - pos = strspn(value, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - pos += strspn(value + pos, DIGITS); - pos += strspn(value + pos, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - - r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos); - if (r != 2) { - log_debug("Failed to parse dual timestamp value \"%s\".", value); - return -EINVAL; - } - - if (value[pos] != '\0') - /* trailing garbage */ - return -EINVAL; - - t->realtime = a; - t->monotonic = b; - - return 0; -} - -int timestamp_deserialize(const char *value, usec_t *timestamp) { - int r; - - assert(value); - - r = safe_atou64(value, timestamp); - if (r < 0) - return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); - - return r; -} - static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { static const struct { const char *name; @@ -1030,7 +974,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { char *b = e + 1; /* Don't allow "0.-0", "3.+1" or "3. 1" */ - if (*b == '-' || *b == '+' || isspace(*b)) + if (IN_SET(*b, '-', '+') || isspace(*b)) return -EINVAL; errno = 0; @@ -1052,12 +996,21 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { something = true; + + k = ((usec_t) -1) / multiplier; + if ((usec_t) l + 1 >= k || (usec_t) z >= k) + return -ERANGE; + k = (usec_t) z * multiplier; for (; n > 0; n--) k /= 10; - r += (usec_t) l * multiplier + k; + k += (usec_t) l * multiplier; + if (k >= ((usec_t) -1) - r) + return -ERANGE; + + r += k; } *usec = r; @@ -1069,18 +1022,19 @@ int parse_sec(const char *t, usec_t *usec) { return parse_time(t, usec, USEC_PER_SEC); } -int parse_sec_fix_0(const char *t, usec_t *usec) { - assert(t); - assert(usec); +int parse_sec_fix_0(const char *t, usec_t *ret) { + usec_t k; + int r; - t += strspn(t, WHITESPACE); + assert(t); + assert(ret); - if (streq(t, "0")) { - *usec = USEC_INFINITY; - return 0; - } + r = parse_sec(t, &k); + if (r < 0) + return r; - return parse_sec(t, usec); + *ret = k == 0 ? USEC_INFINITY : k; + return r; } int parse_nsec(const char *t, nsec_t *nsec) { @@ -1168,7 +1122,7 @@ int parse_nsec(const char *t, nsec_t *nsec) { if (*e == '.') { char *b = e + 1; - if (*b == '-' || *b == '+' || isspace(*b)) + if (IN_SET(*b, '-', '+') || isspace(*b)) return -EINVAL; errno = 0; @@ -1189,12 +1143,22 @@ int parse_nsec(const char *t, nsec_t *nsec) { for (i = 0; i < ELEMENTSOF(table); i++) if (startswith(e, table[i].suffix)) { - nsec_t k = (nsec_t) z * table[i].nsec; + nsec_t k; + + k = ((nsec_t) -1) / table[i].nsec; + if ((nsec_t) l + 1 >= k || (nsec_t) z >= k) + return -ERANGE; + + k = (nsec_t) z * table[i].nsec; for (; n > 0; n--) k /= 10; - r += (nsec_t) l * table[i].nsec + k; + k += (nsec_t) l * table[i].nsec; + if (k >= ((nsec_t) -1) - r) + return -ERANGE; + + r += k; p = e + strlen(table[i].suffix); something = true; @@ -1227,6 +1191,7 @@ int get_timezones(char ***ret) { _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **zones = NULL; size_t n_zones = 0, n_allocated = 0; + int r; assert(ret); @@ -1239,13 +1204,18 @@ int get_timezones(char ***ret) { f = fopen("/usr/share/zoneinfo/zone.tab", "re"); if (f) { - char l[LINE_MAX]; - - FOREACH_LINE(l, f, return -errno) { + for (;;) { + _cleanup_free_ char *line = NULL; char *p, *w; size_t k; - p = strstrip(l); + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + + p = strstrip(line); if (isempty(p) || *p == '#') continue; diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h index 344f2dc52e..5316305062 100644 --- a/src/systemd/src/basic/time-util.h +++ b/src/systemd/src/basic/time-util.h @@ -108,10 +108,6 @@ char *format_timestamp_us_utc(char *buf, size_t l, usec_t t); char *format_timestamp_relative(char *buf, size_t l, usec_t t); char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); -int dual_timestamp_deserialize(const char *value, dual_timestamp *t); -int timestamp_deserialize(const char *value, usec_t *timestamp); - int parse_timestamp(const char *t, usec_t *usec); int parse_sec(const char *t, usec_t *usec); diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c index 40f50039d5..7cff013910 100644 --- a/src/systemd/src/basic/util.c +++ b/src/systemd/src/basic/util.c @@ -25,6 +25,7 @@ #include "def.h" #include "device-nodes.h" #include "dirent-util.h" +#include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" @@ -111,6 +112,7 @@ int prot_from_flags(int flags) { bool in_initrd(void) { struct statfs s; + int r; if (saved_in_initrd >= 0) return saved_in_initrd; @@ -125,9 +127,16 @@ bool in_initrd(void) { * emptying when transititioning to the main systemd. */ - saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && - statfs("/", &s) >= 0 && - is_temporary_fs(&s); + r = getenv_bool_secure("SYSTEMD_IN_INITRD"); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m"); + + if (r >= 0) + saved_in_initrd = r > 0; + else + saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && + statfs("/", &s) >= 0 && + is_temporary_fs(&s); return saved_in_initrd; } diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h index 5f3f982190..8cba4ed726 100644 --- a/src/systemd/src/basic/util.h +++ b/src/systemd/src/basic/util.h @@ -150,7 +150,13 @@ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { int on_ac_power(void); -#define memzero(x,l) (memset((x), 0, (l))) +#define memzero(x,l) \ + ({ \ + size_t _l_ = (l); \ + void *_x_ = (x); \ + _l_ == 0 ? _x_ : memset(_x_, 0, _l_); \ + }) + #define zero(x) (memzero(&(x), sizeof(x))) static inline void *mempset(void *s, int c, size_t n) { diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index 80e9577cd5..8d8cdf9da7 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -80,7 +80,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, .filter = filter }; _cleanup_close_ int s = -1; - int r, on = 1; + int r; assert(ifindex > 0); assert(link); @@ -89,9 +89,9 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, if (s < 0) return -errno; - r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on)); + r = setsockopt_int(s, SOL_PACKET, PACKET_AUXDATA, true); if (r < 0) - return -errno; + return r; r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); if (r < 0) @@ -151,19 +151,19 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { }; _cleanup_close_ int s = -1; char ifname[IF_NAMESIZE] = ""; - int r, on = 1, tos = IPTOS_CLASS_CS6; + int r; s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true); if (r < 0) - return -errno; + return r; if (ifindex > 0) { if (if_indextoname(ifindex, ifname) == 0) @@ -175,18 +175,18 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { } if (address == INADDR_ANY) { - r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true); if (r < 0) - return -errno; + return r; } else { - r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true); if (r < 0) - return -errno; + return r; } r = bind(s, &src.sa, sizeof(src.in)); diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c index 98aa6261eb..22e4201746 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-network.c +++ b/src/systemd/src/libsystemd-network/dhcp6-network.c @@ -27,7 +27,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { .in6.sin6_scope_id = index, }; _cleanup_close_ int s = -1; - int r, off = 0, on = 1; + int r; assert(index > 0); assert(local_address); @@ -38,17 +38,17 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true); if (r < 0) - return -errno; + return r; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); + r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true); if (r < 0) - return -errno; + return r; r = bind(s, &src.sa, sizeof(src.in6)); if (r < 0) diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 7fd57cfe3c..6b77116969 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -88,7 +88,7 @@ struct sd_dhcp_client { uint32_t mtu; uint32_t xid; usec_t start_time; - unsigned int attempt; + unsigned attempt; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 4c2f62a37d..714591c3fb 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -316,6 +316,7 @@ static sd_event *event_resolve(sd_event *e) { static int pending_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; + int r; assert(x->pending); assert(y->pending); @@ -327,22 +328,17 @@ static int pending_prioq_compare(const void *a, const void *b) { return 1; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; + r = CMP(x->priority, y->priority); + if (r != 0) + return r; /* Older entries first */ - if (x->pending_iteration < y->pending_iteration) - return -1; - if (x->pending_iteration > y->pending_iteration) - return 1; - - return 0; + return CMP(x->pending_iteration, y->pending_iteration); } static int prepare_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; + int r; assert(x->prepare); assert(y->prepare); @@ -356,18 +352,12 @@ static int prepare_prioq_compare(const void *a, const void *b) { /* Move most recently prepared ones last, so that we can stop * preparing as soon as we hit one that has already been * prepared in the current iteration */ - if (x->prepare_iteration < y->prepare_iteration) - return -1; - if (x->prepare_iteration > y->prepare_iteration) - return 1; + r = CMP(x->prepare_iteration, y->prepare_iteration); + if (r != 0) + return r; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; + return CMP(x->priority, y->priority); } static int earliest_time_prioq_compare(const void *a, const void *b) { @@ -389,12 +379,7 @@ static int earliest_time_prioq_compare(const void *a, const void *b) { return 1; /* Order by time */ - if (x->time.next < y->time.next) - return -1; - if (x->time.next > y->time.next) - return 1; - - return 0; + return CMP(x->time.next, y->time.next); } static usec_t time_event_source_latest(const sd_event_source *s) { @@ -420,12 +405,7 @@ static int latest_time_prioq_compare(const void *a, const void *b) { return 1; /* Order by time */ - if (time_event_source_latest(x) < time_event_source_latest(y)) - return -1; - if (time_event_source_latest(x) > time_event_source_latest(y)) - return 1; - - return 0; + return CMP(time_event_source_latest(x), time_event_source_latest(y)); } static int exit_prioq_compare(const void *a, const void *b) { @@ -441,12 +421,7 @@ static int exit_prioq_compare(const void *a, const void *b) { return 1; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; + return CMP(x->priority, y->priority); } static void free_clock_data(struct clock_data *d) { @@ -1594,21 +1569,16 @@ static int event_make_inotify_data( static int inode_data_compare(const void *a, const void *b) { const struct inode_data *x = a, *y = b; + int r; assert(x); assert(y); - if (x->dev < y->dev) - return -1; - if (x->dev > y->dev) - return 1; - - if (x->ino < y->ino) - return -1; - if (x->ino > y->ino) - return 1; + r = CMP(x->dev, y->dev); + if (r != 0) + return r; - return 0; + return CMP(x->ino, y->ino); } static void inode_data_hash_func(const void *p, struct siphash *state) { diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index eb35b83431..c38eb84beb 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -33,7 +33,8 @@ - Supports event source prioritization - Scales better with a large number of time events because it does not require one timerfd each - Automatically tries to coalesce timer events system-wide - - Handles signals and child PIDs + - Handles signals, child PIDs, inotify events + - Supports systemd-style automatic watchdog event generation */ _SD_BEGIN_DECLARATIONS; |