diff options
| author | Michael Biebl <biebl@debian.org> | 2020-04-11 21:28:04 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2020-04-11 21:28:04 +0200 |
| commit | 1e5977b62f896e844b548c3007ace9e1dfa7f9ed (patch) | |
| tree | 7a7416ed410e72b6200f3d860fd315ec11cc106b /shared/systemd | |
| parent | b012fa6e1d808e0736c009799c62d835cbfcc1dd (diff) | |
New upstream version 1.23.90 upstream/1.23.90
Diffstat (limited to 'shared/systemd')
40 files changed, 2152 insertions, 619 deletions
diff --git a/shared/systemd/sd-adapt-shared/missing_syscall.h b/shared/systemd/sd-adapt-shared/arphrd-list.h index 637892c2..637892c2 100644 --- a/shared/systemd/sd-adapt-shared/missing_syscall.h +++ b/shared/systemd/sd-adapt-shared/arphrd-list.h diff --git a/shared/systemd/sd-adapt-shared/missing_keyctl.h b/shared/systemd/sd-adapt-shared/missing_keyctl.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/shared/systemd/sd-adapt-shared/missing_keyctl.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h index 490cae92..26a043f5 100644 --- a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h +++ b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: LGPL-2.1+ /* * Copyright (C) 2014 - 2018 Red Hat, Inc. */ @@ -84,6 +84,8 @@ G_STMT_START { \ #include <sys/syscall.h> #include <sys/ioctl.h> +#define ENABLE_GSHADOW FALSE + /*****************************************************************************/ /* systemd cannot be compiled with "-Wdeclaration-after-statement". In particular @@ -122,6 +124,24 @@ static inline pid_t _nm_gettid(void) { #define HAVE_EXPLICIT_BZERO 0 #endif +#if defined(HAVE_DECL_PIDFD_OPEN) && HAVE_DECL_PIDFD_OPEN == 1 +#define HAVE_PIDFD_OPEN 1 +#else +#define HAVE_PIDFD_OPEN 0 +#endif + +#if defined(HAVE_DECL_PIDFD_SEND_SIGNAL) && HAVE_DECL_PIDFD_SEND_SIGNAL == 1 +#define HAVE_PIDFD_SEND_SIGNAL 1 +#else +#define HAVE_PIDFD_SEND_SIGNAL 0 +#endif + +#if defined(HAVE_DECL_RT_SIGQUEUEINFO) && HAVE_DECL_RT_SIGQUEUEINFO == 1 +#define HAVE_RT_SIGQUEUEINFO 1 +#else +#define HAVE_RT_SIGQUEUEINFO 0 +#endif + #endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ /*****************************************************************************/ diff --git a/shared/systemd/src/basic/cgroup-util.h b/shared/systemd/src/basic/cgroup-util.h new file mode 100644 index 00000000..237139fa --- /dev/null +++ b/shared/systemd/src/basic/cgroup-util.h @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <dirent.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/statfs.h> +#include <sys/types.h> + +#include "def.h" +#include "set.h" + +#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd" +#define SYSTEMD_CGROUP_CONTROLLER_HYBRID "name=unified" +#define SYSTEMD_CGROUP_CONTROLLER "_systemd" + +/* An enum of well known cgroup controllers */ +typedef enum CGroupController { + /* Original cgroup controllers */ + CGROUP_CONTROLLER_CPU, + CGROUP_CONTROLLER_CPUACCT, /* v1 only */ + CGROUP_CONTROLLER_CPUSET, /* v2 only */ + CGROUP_CONTROLLER_IO, /* v2 only */ + CGROUP_CONTROLLER_BLKIO, /* v1 only */ + CGROUP_CONTROLLER_MEMORY, + CGROUP_CONTROLLER_DEVICES, /* v1 only */ + CGROUP_CONTROLLER_PIDS, + + /* BPF-based pseudo-controllers, v2 only */ + CGROUP_CONTROLLER_BPF_FIREWALL, + CGROUP_CONTROLLER_BPF_DEVICES, + + _CGROUP_CONTROLLER_MAX, + _CGROUP_CONTROLLER_INVALID = -1, +} CGroupController; + +#define CGROUP_CONTROLLER_TO_MASK(c) (1U << (c)) + +/* A bit mask of well known cgroup controllers */ +typedef enum CGroupMask { + CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU), + CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT), + CGROUP_MASK_CPUSET = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUSET), + CGROUP_MASK_IO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_IO), + CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO), + CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY), + CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES), + CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS), + CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL), + CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES), + + /* All real cgroup v1 controllers */ + CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS, + + /* All real cgroup v2 controllers */ + CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS, + + /* All cgroup v2 BPF pseudo-controllers */ + CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES, + + _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 +} CGroupMask; + +static inline CGroupMask CGROUP_MASK_EXTEND_JOINED(CGroupMask mask) { + /* We always mount "cpu" and "cpuacct" in the same hierarchy. Hence, when one bit is set also set the other */ + + if (mask & (CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT)) + mask |= (CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT); + + return mask; +} + +CGroupMask get_cpu_accounting_mask(void); +bool cpu_accounting_is_cheap(void); + +/* Special values for all weight knobs on unified hierarchy */ +#define CGROUP_WEIGHT_INVALID ((uint64_t) -1) +#define CGROUP_WEIGHT_MIN UINT64_C(1) +#define CGROUP_WEIGHT_MAX UINT64_C(10000) +#define CGROUP_WEIGHT_DEFAULT UINT64_C(100) + +#define CGROUP_LIMIT_MIN UINT64_C(0) +#define CGROUP_LIMIT_MAX ((uint64_t) -1) + +static inline bool CGROUP_WEIGHT_IS_OK(uint64_t x) { + return + x == CGROUP_WEIGHT_INVALID || + (x >= CGROUP_WEIGHT_MIN && x <= CGROUP_WEIGHT_MAX); +} + +/* IO limits on unified hierarchy */ +typedef enum CGroupIOLimitType { + CGROUP_IO_RBPS_MAX, + CGROUP_IO_WBPS_MAX, + CGROUP_IO_RIOPS_MAX, + CGROUP_IO_WIOPS_MAX, + + _CGROUP_IO_LIMIT_TYPE_MAX, + _CGROUP_IO_LIMIT_TYPE_INVALID = -1 +} CGroupIOLimitType; + +extern const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX]; + +const char* cgroup_io_limit_type_to_string(CGroupIOLimitType t) _const_; +CGroupIOLimitType cgroup_io_limit_type_from_string(const char *s) _pure_; + +/* Special values for the cpu.shares attribute */ +#define CGROUP_CPU_SHARES_INVALID ((uint64_t) -1) +#define CGROUP_CPU_SHARES_MIN UINT64_C(2) +#define CGROUP_CPU_SHARES_MAX UINT64_C(262144) +#define CGROUP_CPU_SHARES_DEFAULT UINT64_C(1024) + +static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) { + return + x == CGROUP_CPU_SHARES_INVALID || + (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX); +} + +/* Special values for the blkio.weight attribute */ +#define CGROUP_BLKIO_WEIGHT_INVALID ((uint64_t) -1) +#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10) +#define CGROUP_BLKIO_WEIGHT_MAX UINT64_C(1000) +#define CGROUP_BLKIO_WEIGHT_DEFAULT UINT64_C(500) + +static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) { + return + x == CGROUP_BLKIO_WEIGHT_INVALID || + (x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX); +} + +typedef enum CGroupUnified { + CGROUP_UNIFIED_UNKNOWN = -1, + CGROUP_UNIFIED_NONE = 0, /* Both systemd and controllers on legacy */ + CGROUP_UNIFIED_SYSTEMD = 1, /* Only systemd on unified */ + CGROUP_UNIFIED_ALL = 2, /* Both systemd and controllers on unified */ +} CGroupUnified; + +/* + * General rules: + * + * We accept named hierarchies in the syntax "foo" and "name=foo". + * + * We expect that named hierarchies do not conflict in name with a + * kernel hierarchy, modulo the "name=" prefix. + * + * We always generate "normalized" controller names, i.e. without the + * "name=" prefix. + * + * We require absolute cgroup paths. When returning, we will always + * generate paths with multiple adjacent / removed. + */ + +int cg_enumerate_processes(const char *controller, const char *path, FILE **_f); +int cg_read_pid(FILE *f, pid_t *_pid); +int cg_read_event(const char *controller, const char *path, const char *event, + char **val); + +int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d); +int cg_read_subgroup(DIR *d, char **fn); + +typedef enum CGroupFlags { + CGROUP_SIGCONT = 1 << 0, + CGROUP_IGNORE_SELF = 1 << 1, + CGROUP_REMOVE = 1 << 2, +} CGroupFlags; + +typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata); + +int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); +int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); + +int cg_split_spec(const char *spec, char **ret_controller, char **ret_path); +int cg_mangle_path(const char *path, char **result); + +int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs); +int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs); + +int cg_pid_get_path(const char *controller, pid_t pid, char **path); + +int cg_rmdir(const char *controller, const char *path); + +int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); +int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); +int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values); + +int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret); + +int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid); + +int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags); +int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size); +int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret); +int cg_remove_xattr(const char *controller, const char *path, const char *name); + +int cg_install_release_agent(const char *controller, const char *agent); +int cg_uninstall_release_agent(const char *controller); + +int cg_is_empty(const char *controller, const char *path); +int cg_is_empty_recursive(const char *controller, const char *path); + +int cg_get_root_path(char **path); + +int cg_path_get_session(const char *path, char **session); +int cg_path_get_owner_uid(const char *path, uid_t *uid); +int cg_path_get_unit(const char *path, char **unit); +int cg_path_get_user_unit(const char *path, char **unit); +int cg_path_get_machine_name(const char *path, char **machine); +int cg_path_get_slice(const char *path, char **slice); +int cg_path_get_user_slice(const char *path, char **slice); + +int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted); +int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup); + +int cg_pid_get_session(pid_t pid, char **session); +int cg_pid_get_owner_uid(pid_t pid, uid_t *uid); +int cg_pid_get_unit(pid_t pid, char **unit); +int cg_pid_get_user_unit(pid_t pid, char **unit); +int cg_pid_get_machine_name(pid_t pid, char **machine); +int cg_pid_get_slice(pid_t pid, char **slice); +int cg_pid_get_user_slice(pid_t pid, char **slice); + +int cg_path_decode_unit(const char *cgroup, char **unit); + +char *cg_escape(const char *p); +char *cg_unescape(const char *p) _pure_; + +bool cg_controller_is_valid(const char *p); + +int cg_slice_to_path(const char *unit, char **ret); + +typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata); + +int cg_mask_supported(CGroupMask *ret); +int cg_mask_from_string(const char *s, CGroupMask *ret); +int cg_mask_to_string(CGroupMask mask, char **ret); + +int cg_kernel_controllers(Set **controllers); + +bool cg_ns_supported(void); + +int cg_all_unified(void); +int cg_hybrid_unified(void); +int cg_unified_controller(const char *controller); +int cg_unified_cached(bool flush); +static inline int cg_unified(void) { + return cg_unified_cached(true); +} + +const char* cgroup_controller_to_string(CGroupController c) _const_; +CGroupController cgroup_controller_from_string(const char *s) _pure_; + +bool is_cgroup_fs(const struct statfs *s); +bool fd_is_cgroup_fs(int fd); diff --git a/shared/systemd/src/basic/errno-util.h b/shared/systemd/src/basic/errno-util.h index 8f1be6c0..65a6384e 100644 --- a/shared/systemd/src/basic/errno-util.h +++ b/shared/systemd/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/shared/systemd/src/basic/escape.c b/shared/systemd/src/basic/escape.c index 06d823c7..2cc5be18 100644 --- a/shared/systemd/src/basic/escape.c +++ b/shared/systemd/src/basic/escape.c @@ -104,7 +104,7 @@ char *cescape(const char *s) { return cescape_length(s, strlen(s)); } -int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) { +int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) { int r = 1; assert(p); @@ -173,7 +173,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) return -EINVAL; /* Don't allow NUL bytes */ - if (a == 0 && b == 0) + if (a == 0 && b == 0 && !accept_nul) return -EINVAL; *ret = (a << 4U) | b; @@ -201,7 +201,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3]; /* Don't allow 0 chars */ - if (c == 0) + if (c == 0 && !accept_nul) return -EINVAL; *ret = c; @@ -229,7 +229,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7]; /* Don't allow 0 chars */ - if (c == 0) + if (c == 0 && !accept_nul) return -EINVAL; /* Don't allow invalid code points */ @@ -269,7 +269,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) return -EINVAL; /* don't allow NUL bytes */ - if (a == 0 && b == 0 && c == 0) + if (a == 0 && b == 0 && c == 0 && !accept_nul) return -EINVAL; /* Don't allow bytes above 255 */ @@ -290,6 +290,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) return r; } +#if 0 /* NM_IGNORED */ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) { char *r, *t; const char *f; @@ -335,7 +336,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi return -EINVAL; } - k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit); + k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL); if (k < 0) { if (flags & UNESCAPE_RELAX) { /* Invalid escape code, let's take it literal then */ @@ -362,15 +363,6 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi return t - r; } -int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) { - return cunescape_length_with_prefix(s, length, NULL, flags, ret); -} - -int cunescape(const char *s, UnescapeFlags flags, char **ret) { - return cunescape_length(s, strlen(s), flags, ret); -} - -#if 0 /* NM_IGNORED */ char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) { char *ans, *t, *prev, *prev2; const char *f; diff --git a/shared/systemd/src/basic/escape.h b/shared/systemd/src/basic/escape.h index b26054c5..b8eb137c 100644 --- a/shared/systemd/src/basic/escape.h +++ b/shared/systemd/src/basic/escape.h @@ -29,22 +29,27 @@ #define SHELL_NEED_ESCAPE_POSIX "\\\'" typedef enum UnescapeFlags { - UNESCAPE_RELAX = 1, + UNESCAPE_RELAX = 1 << 0, + UNESCAPE_ACCEPT_NUL = 1 << 1, } UnescapeFlags; typedef enum EscapeStyle { ESCAPE_BACKSLASH = 1, - ESCAPE_POSIX = 2, + ESCAPE_POSIX = 2, } EscapeStyle; char *cescape(const char *s); char *cescape_length(const char *s, size_t n); int cescape_char(char c, char *buf); -int cunescape(const char *s, UnescapeFlags flags, char **ret); -int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret); int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret); -int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit); +static inline int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) { + return cunescape_length_with_prefix(s, length, NULL, flags, ret); +} +static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) { + return cunescape_length(s, strlen(s), flags, ret); +} +int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul); char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits); static inline char *xescape(const char *s, const char *bad) { diff --git a/shared/systemd/src/basic/extract-word.c b/shared/systemd/src/basic/extract-word.c index 2da25b03..62f015f3 100644 --- a/shared/systemd/src/basic/extract-word.c +++ b/shared/systemd/src/basic/extract-word.c @@ -92,7 +92,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra bool eight_bit = false; char32_t u; - r = cunescape_one(*p, (size_t) -1, &u, &eight_bit); + r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false); if (r < 0) { if (flags & EXTRACT_CUNESCAPE_RELAX) { s[sz++] = '\\'; diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c index 3bffa1c5..543dd4eb 100644 --- a/shared/systemd/src/basic/fileio.c +++ b/shared/systemd/src/basic/fileio.c @@ -56,6 +56,44 @@ int fdopen_unlocked(int fd, const char *options, FILE **ret) { return 0; } +int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) { + int r; + + assert(fd); + + r = fdopen_unlocked(*fd, options, ret); + if (r < 0) + return r; + + *fd = -1; + + return 0; +} + +FILE* take_fdopen(int *fd, const char *options) { + assert(fd); + + FILE *f = fdopen(*fd, options); + if (!f) + return NULL; + + *fd = -1; + + return f; +} + +DIR* take_fdopendir(int *dfd) { + assert(dfd); + + DIR *d = fdopendir(*dfd); + if (!d) + return NULL; + + *dfd = -1; + + return d; +} + FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc) { FILE *f = open_memstream(ptr, sizeloc); if (!f) @@ -139,16 +177,21 @@ static int write_string_file_atomic( assert(fn); assert(line); + /* Note that we'd really like to use O_TMPFILE here, but can't really, since we want replacement + * semantics here, and O_TMPFILE can't offer that. i.e. rename() replaces but linkat() doesn't. */ + r = fopen_temporary(fn, &f, &p); if (r < 0) return r; - (void) fchmod_umask(fileno(f), 0644); - r = write_string_stream_ts(f, line, flags, ts); if (r < 0) goto fail; + r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644); + if (r < 0) + goto fail; + if (rename(p, fn) < 0) { r = -errno; goto fail; @@ -168,7 +211,7 @@ int write_string_file_ts( struct timespec *ts) { _cleanup_fclose_ FILE *f = NULL; - int q, r; + int q, r, fd; assert(fn); assert(line); @@ -193,26 +236,20 @@ int write_string_file_ts( } else assert(!ts); - if (flags & WRITE_STRING_FILE_CREATE) { - r = fopen_unlocked(fn, "we", &f); - if (r < 0) - goto fail; - } else { - int fd; - - /* We manually build our own version of fopen(..., "we") that - * works without O_CREAT */ - fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY | ((flags & WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0)); - if (fd < 0) { - r = -errno; - goto fail; - } + /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */ + fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY | + (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) | + (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0), + (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666)); + if (fd < 0) { + r = -errno; + goto fail; + } - r = fdopen_unlocked(fd, "w", &f); - if (r < 0) { - safe_close(fd); - goto fail; - } + r = fdopen_unlocked(fd, "w", &f); + if (r < 0) { + safe_close(fd); + goto fail; } if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) @@ -547,17 +584,19 @@ finalize: return r; } -int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) { +int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) { _cleanup_fclose_ FILE *f = NULL; int r; assert(filename); assert(contents); - r = fopen_unlocked(filename, "re", &f); + r = xfopenat(dir_fd, filename, "re", 0, &f); if (r < 0) return r; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + return read_full_stream_full(f, filename, flags, contents, size); } @@ -686,6 +725,81 @@ DIR *xopendirat(int fd, const char *name, int flags) { return d; } +static int mode_to_flags(const char *mode) { + const char *p; + int flags; + + if ((p = startswith(mode, "r+"))) + flags = O_RDWR; + else if ((p = startswith(mode, "r"))) + flags = O_RDONLY; + else if ((p = startswith(mode, "w+"))) + flags = O_RDWR|O_CREAT|O_TRUNC; + else if ((p = startswith(mode, "w"))) + flags = O_WRONLY|O_CREAT|O_TRUNC; + else if ((p = startswith(mode, "a+"))) + flags = O_RDWR|O_CREAT|O_APPEND; + else if ((p = startswith(mode, "a"))) + flags = O_WRONLY|O_CREAT|O_APPEND; + else + return -EINVAL; + + for (; *p != 0; p++) { + + switch (*p) { + + case 'e': + flags |= O_CLOEXEC; + break; + + case 'x': + flags |= O_EXCL; + break; + + case 'm': + /* ignore this here, fdopen() might care later though */ + break; + + case 'c': /* not sure what to do about this one */ + default: + return -EINVAL; + } + } + + return flags; +} + +int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret) { + FILE *f; + + /* A combination of fopen() with openat() */ + + if (dir_fd == AT_FDCWD && flags == 0) { + f = fopen(path, mode); + if (!f) + return -errno; + } else { + int fd, mode_flags; + + mode_flags = mode_to_flags(mode); + if (mode_flags < 0) + return mode_flags; + + fd = openat(dir_fd, path, mode_flags | flags); + if (fd < 0) + return -errno; + + f = fdopen(fd, mode); + if (!f) { + safe_close(fd); + return -errno; + } + } + + *ret = f; + return 0; +} + #if 0 /* NM_IGNORED */ static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) { char **i; diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h index 31bfef33..58daabaa 100644 --- a/shared/systemd/src/basic/fileio.h +++ b/shared/systemd/src/basic/fileio.h @@ -6,6 +6,7 @@ #include <stddef.h> #include <stdio.h> #include <sys/stat.h> +#include <sys/fcntl.h> #include <sys/types.h> #include "macro.h" @@ -22,6 +23,7 @@ typedef enum { WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5, WRITE_STRING_FILE_NOFOLLOW = 1 << 6, WRITE_STRING_FILE_MKDIR_0755 = 1 << 7, + WRITE_STRING_FILE_MODE_0600 = 1 << 8, /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() @@ -37,6 +39,9 @@ typedef enum { int fopen_unlocked(const char *path, const char *options, FILE **ret); int fdopen_unlocked(int fd, const char *options, FILE **ret); +int take_fdopen_unlocked(int *fd, const char *options, FILE **ret); +FILE* take_fdopen(int *fd, const char *options); +DIR* take_fdopendir(int *dfd); FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc); FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode); @@ -52,9 +57,9 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); int read_one_line_file(const char *filename, char **line); -int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); +int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); static inline int read_full_file(const char *filename, char **contents, size_t *size) { - return read_full_file_full(filename, 0, contents, size); + return read_full_file_full(AT_FDCWD, filename, 0, contents, size); } int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); @@ -69,6 +74,7 @@ int executable_is_script(const char *path, char **interpreter); int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); DIR *xopendirat(int dirfd, const char *name, int flags); +int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret); 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); diff --git a/shared/systemd/src/basic/format-util.h b/shared/systemd/src/basic/format-util.h index 59622508..c47fa76e 100644 --- a/shared/systemd/src/basic/format-util.h +++ b/shared/systemd/src/basic/format-util.h @@ -5,30 +5,18 @@ #include <net/if.h> #include <stdbool.h> -#if SIZEOF_PID_T == 4 -# define PID_PRI PRIi32 -#elif SIZEOF_PID_T == 2 -# define PID_PRI PRIi16 -#else -# error Unknown pid_t size -#endif +#include "cgroup-util.h" +#include "macro.h" + +assert_cc(sizeof(pid_t) == sizeof(int32_t)); +#define PID_PRI PRIi32 #define PID_FMT "%" PID_PRI -#if SIZEOF_UID_T == 4 -# define UID_FMT "%" PRIu32 -#elif SIZEOF_UID_T == 2 -# define UID_FMT "%" PRIu16 -#else -# error Unknown uid_t size -#endif +assert_cc(sizeof(uid_t) == sizeof(uint32_t)); +#define UID_FMT "%" PRIu32 -#if SIZEOF_GID_T == 4 -# define GID_FMT "%" PRIu32 -#elif SIZEOF_GID_T == 2 -# define GID_FMT "%" PRIu16 -#else -# error Unknown gid_t size -#endif +assert_cc(sizeof(gid_t) == sizeof(uint32_t)); +#define GID_FMT "%" PRIu32 #if SIZEOF_TIME_T == 8 # define PRI_TIME PRIi64 @@ -84,8 +72,15 @@ typedef enum { FORMAT_BYTES_TRAILING_B = 1 << 2, } FormatBytesFlag; -#define FORMAT_BYTES_MAX 8 +#define FORMAT_BYTES_MAX 16 char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag); static inline char *format_bytes(char *buf, size_t l, uint64_t t) { return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B); } +static inline char *format_bytes_cgroup_protection(char *buf, size_t l, uint64_t t) { + if (t == CGROUP_LIMIT_MAX) { + (void) snprintf(buf, l, "%s", "infinity"); + return buf; + } + return format_bytes(buf, l, t); +} diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c index e30f40fb..79966571 100644 --- a/shared/systemd/src/basic/fs-util.c +++ b/shared/systemd/src/basic/fs-util.c @@ -276,7 +276,52 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { return do_chown || do_chmod; } -#endif /* NM_IGNORED */ + +int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid) { + bool do_chown, do_chmod; + struct stat st; + + assert(path); + + /* Change ownership and access mode of the specified path, see description of fchmod_and_chown(). + * Should only be used on trusted paths. */ + + if (lstat(path, &st) < 0) + return -errno; + + do_chown = + (uid != UID_INVALID && st.st_uid != uid) || + (gid != GID_INVALID && st.st_gid != gid); + + do_chmod = + !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */ + ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) || + do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown() + * modifies the access mode too */ + + if (mode == MODE_INVALID) + mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */ + else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0) + return -EINVAL; /* insist on the right file type if it was specified */ + + if (do_chown && do_chmod) { + mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */ + + if (((minimal ^ st.st_mode) & 07777) != 0) + if (chmod(path, minimal & 07777) < 0) + return -errno; + } + + if (do_chown) + if (lchown(path, uid, gid) < 0) + return -errno; + + if (do_chmod) + if (chmod(path, mode & 07777) < 0) + return -errno; + + return do_chown || do_chmod; +} int fchmod_umask(int fd, mode_t m) { mode_t u; @@ -289,7 +334,6 @@ int fchmod_umask(int fd, mode_t m) { return r; } -#if 0 /* NM_IGNORED */ int fchmod_opath(int fd, mode_t m) { char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; @@ -551,6 +595,7 @@ int get_files_in_directory(const char *path, char ***list) { return n; } +#endif /* NM_IGNORED */ static int getenv_tmp_dir(const char **ret_path) { const char *n; @@ -622,6 +667,7 @@ static int tmp_dir_internal(const char *def, const char **ret) { return 0; } +#if 0 /* NM_IGNORED */ int var_tmp_dir(const char **ret) { /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus @@ -631,6 +677,7 @@ int var_tmp_dir(const char **ret) { return tmp_dir_internal("/var/tmp", ret); } +#endif /* NM_IGNORED */ int tmp_dir(const char **ret) { @@ -640,6 +687,7 @@ int tmp_dir(const char **ret) { return tmp_dir_internal("/tmp", ret); } +#if 0 /* NM_IGNORED */ int unlink_or_warn(const char *filename) { if (unlink(filename) < 0 && errno != ENOENT) /* If the file doesn't exist and the fs simply was read-only (in which @@ -806,6 +854,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (r < 0) return r; + /* Simplify the root directory, so that it has no duplicate slashes and nothing at the + * end. While we won't resolve the root path we still simplify it. Note that dropping the + * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY + * anyway. Moreover at the end of this function after processing everything we'll always turn + * the empty string back to "/". */ + delete_trailing_chars(root, "/"); + path_simplify(root, true); + if (flags & CHASE_PREFIX_ROOT) { /* We don't support relative paths in combination with a root directory */ if (!path_is_absolute(path)) @@ -819,7 +875,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (r < 0) return r; - fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH); + fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); if (fd < 0) return -errno; @@ -828,6 +884,31 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return -errno; } + if (root) { + _cleanup_free_ char *absolute = NULL; + const char *e; + + /* If we are operating on a root directory, let's take the root directory as it is. */ + + e = path_startswith(buffer, root); + if (!e) + return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG, + SYNTHETIC_ERRNO(ECHRNG), + "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.", + path, root); + + done = strdup(root); + if (!done) + return -ENOMEM; + + /* Make sure "todo" starts with a slash */ + absolute = strjoin("/", e); + if (!absolute) + return -ENOMEM; + + free_and_replace(buffer, absolute); + } + todo = buffer; for (;;) { _cleanup_free_ char *first = NULL; @@ -837,6 +918,15 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, /* Determine length of first component in the path */ n = strspn(todo, "/"); /* The slashes */ + + if (n > 1) { + /* If we are looking at more than a single slash then skip all but one, so that when + * we are done with everything we have a normalized path with only single slashes + * separating the path components. */ + todo += n - 1; + n = 1; + } + m = n + strcspn(todo + n, "/"); /* The entire length of the component */ /* Extract the first component. */ @@ -939,7 +1029,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; if ((flags & CHASE_SAFE) && - (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) && unsafe_transition(&previous_stat, &st)) return log_unsafe_transition(fd, child, path, flags); @@ -970,7 +1059,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * directory as base. */ safe_close(fd); - fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH); + fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); if (fd < 0) return -errno; diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h index 78d68be9..6b9ade2e 100644 --- a/shared/systemd/src/basic/fs-util.h +++ b/shared/systemd/src/basic/fs-util.h @@ -34,6 +34,7 @@ int readlink_and_make_absolute(const char *p, char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid); +int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_umask(int fd, mode_t mode); int fchmod_opath(int fd, mode_t m); diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c index 91d687c2..0e770621 100644 --- a/shared/systemd/src/basic/in-addr-util.c +++ b/shared/systemd/src/basic/in-addr-util.c @@ -445,54 +445,6 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union return -EINVAL; } -#if 0 /* NM_IGNORED */ -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { - _cleanup_free_ char *buf = NULL; - const char *suffix; - int r, ifi = 0; - - assert(s); - assert(family); - assert(ret); - - /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") - * if one is found. */ - - suffix = strchr(s, '%'); - if (suffix) { - - if (ifindex) { - /* If we shall return the interface index, try to parse it */ - r = parse_ifindex(suffix + 1, &ifi); - if (r < 0) { - unsigned u; - - u = if_nametoindex(suffix + 1); - if (u <= 0) - return -errno; - - ifi = (int) u; - } - } - - buf = strndup(s, suffix - s); - if (!buf) - return -ENOMEM; - - s = buf; - } - - r = in_addr_from_string_auto(s, family, ret); - if (r < 0) - return r; - - if (ifindex) - *ifindex = ifi; - - return r; -} -#endif /* NM_IGNORED */ - unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { assert(addr); diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h index 28afc7d8..ae2dad0b 100644 --- a/shared/systemd/src/basic/in-addr-util.h +++ b/shared/systemd/src/basic/in-addr-util.h @@ -42,7 +42,7 @@ int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); + unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr); struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); diff --git a/shared/systemd/src/basic/log.h b/shared/systemd/src/basic/log.h index b81c93a5..3725b08f 100644 --- a/shared/systemd/src/basic/log.h +++ b/shared/systemd/src/basic/log.h @@ -59,9 +59,12 @@ void log_show_color(bool b); bool log_get_show_color(void) _pure_; void log_show_location(bool b); bool log_get_show_location(void) _pure_; +void log_show_time(bool b); +bool log_get_show_time(void) _pure_; int log_show_color_from_string(const char *e); int log_show_location_from_string(const char *e); +int log_show_time_from_string(const char *e); LogTarget log_get_target(void) _pure_; #if 0 /* NM_IGNORED */ @@ -77,7 +80,7 @@ int log_get_max_level_realm(LogRealm realm) _pure_; #if 0 /* NM_IGNORED */ assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1); -#define PROJECT_FILE (__FILE__ + STRLEN(RELATIVE_SOURCE_PATH) + 1) +#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1]) #endif /* NM_IGNORED */ #define PROJECT_FILE __FILE__ diff --git a/shared/systemd/src/basic/macro.h b/shared/systemd/src/basic/macro.h index fc733366..6cd9c516 100644 --- a/shared/systemd/src/basic/macro.h +++ b/shared/systemd/src/basic/macro.h @@ -172,6 +172,11 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) { /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ static inline unsigned long ALIGN_POWER2(unsigned long u) { + + /* Avoid subtraction overflow */ + if (u == 0) + return 0; + /* clz(0) is undefined */ if (u == 1) return 1; @@ -183,6 +188,29 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); } +static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) { + size_t m; + + /* Round up allocation sizes a bit to some reasonable, likely larger value. This is supposed to be + * used for cases which are likely called in an allocation loop of some form, i.e. that repetitively + * grow stuff, for example strv_extend() and suchlike. + * + * Note the difference to GREEDY_REALLOC() here, as this helper operates on a single size value only, + * and rounds up to next multiple of 2, needing no further counter. + * + * Note the benefits of direct ALIGN_POWER2() usage: type-safety for size_t, sane handling for very + * small (i.e. <= 2) and safe handling for very large (i.e. > SSIZE_MAX) values. */ + + if (l <= 2) + return 2; /* Never allocate less than 2 of something. */ + + m = ALIGN_POWER2(l); + if (m == 0) /* overflow? */ + return l; + + return m; +} + #ifndef __COVERITY__ # define VOID_0 ((void)0) #else diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h index 46a6907a..b7e2e67e 100644 --- a/shared/systemd/src/basic/memory-util.h +++ b/shared/systemd/src/basic/memory-util.h @@ -7,6 +7,7 @@ #include <string.h> #include <sys/types.h> +#include "alloc-util.h" #include "macro.h" size_t page_size(void) _pure_; @@ -80,14 +81,19 @@ 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 (!p) + return NULL; - if (ptr) { - size_t l = malloc_usable_size(ptr); - explicit_bzero_safe(ptr, l); - free(ptr); - } + l = malloc_usable_size(p); + explicit_bzero_safe(p, l); + return mfree(p); +} + +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/shared/systemd/src/basic/missing_syscall.h b/shared/systemd/src/basic/missing_syscall.h new file mode 100644 index 00000000..4c42e665 --- /dev/null +++ b/shared/systemd/src/basic/missing_syscall.h @@ -0,0 +1,574 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +/* Missing glibc definitions to access certain kernel APIs */ + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#ifdef ARCH_MIPS +#include <asm/sgidefs.h> +#endif + +#include "missing_keyctl.h" +#include "missing_stat.h" + +#if 0 /* NM_IGNORED */ + +/* linux/kcmp.h */ +#ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */ +#define KCMP_FILE 0 +#endif + +#if !HAVE_PIVOT_ROOT +static inline int missing_pivot_root(const char *new_root, const char *put_old) { + return syscall(__NR_pivot_root, new_root, put_old); +} + +# define pivot_root missing_pivot_root +#endif + +/* ======================================================================= */ + +#if !HAVE_MEMFD_CREATE +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_memfd_create && __NR_memfd_create >= 0) +# if defined __NR_memfd_create +# undef __NR_memfd_create +# endif +# if defined __x86_64__ +# define __NR_memfd_create 319 +# elif defined __arm__ +# define __NR_memfd_create 385 +# elif defined __aarch64__ +# define __NR_memfd_create 279 +# elif defined __s390__ +# define __NR_memfd_create 350 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_memfd_create 4354 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_memfd_create 6318 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_memfd_create 5314 +# endif +# elif defined __i386__ +# define __NR_memfd_create 356 +# elif defined __arc__ +# define __NR_memfd_create 279 +# else +# warning "__NR_memfd_create unknown for your architecture" +# endif +# endif + +static inline int missing_memfd_create(const char *name, unsigned int flags) { +# ifdef __NR_memfd_create + return syscall(__NR_memfd_create, name, flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define memfd_create missing_memfd_create +#endif + +/* ======================================================================= */ + +#if !HAVE_GETRANDOM +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_getrandom && __NR_getrandom >= 0) +# if defined __NR_getrandom +# undef __NR_getrandom +# endif +# if defined __x86_64__ +# define __NR_getrandom 318 +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__arm__) +# define __NR_getrandom 384 +# elif defined(__aarch64__) +# define __NR_getrandom 278 +# elif defined(__ia64__) +# define __NR_getrandom 1339 +# elif defined(__m68k__) +# define __NR_getrandom 352 +# elif defined(__s390x__) +# define __NR_getrandom 349 +# elif defined(__powerpc__) +# define __NR_getrandom 359 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_getrandom 4353 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_getrandom 6317 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_getrandom 5313 +# endif +# elif defined(__arc__) +# define __NR_getrandom 278 +# else +# warning "__NR_getrandom unknown for your architecture" +# endif +# endif + +static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) { +# ifdef __NR_getrandom + return syscall(__NR_getrandom, buffer, count, flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define getrandom missing_getrandom +#endif + +/* ======================================================================= */ + +#if !HAVE_GETTID +static inline pid_t missing_gettid(void) { + return (pid_t) syscall(__NR_gettid); +} + +# define gettid missing_gettid +#endif + +/* ======================================================================= */ + +#if !HAVE_NAME_TO_HANDLE_AT +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_name_to_handle_at && __NR_name_to_handle_at >= 0) +# if defined __NR_name_to_handle_at +# undef __NR_name_to_handle_at +# endif +# if defined(__x86_64__) +# define __NR_name_to_handle_at 303 +# elif defined(__i386__) +# define __NR_name_to_handle_at 341 +# elif defined(__arm__) +# define __NR_name_to_handle_at 370 +# elif defined(__powerpc__) +# define __NR_name_to_handle_at 345 +# elif defined(__arc__) +# define __NR_name_to_handle_at 264 +# else +# error "__NR_name_to_handle_at is not defined" +# endif +# endif + +struct file_handle { + unsigned int handle_bytes; + int handle_type; + unsigned char f_handle[0]; +}; + +static inline int missing_name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) { +# ifdef __NR_name_to_handle_at + return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define name_to_handle_at missing_name_to_handle_at +#endif + +/* ======================================================================= */ + +#if !HAVE_SETNS +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_setns && __NR_setns >= 0) +# if defined __NR_setns +# undef __NR_setns +# endif +# if defined(__x86_64__) +# define __NR_setns 308 +# elif defined(__i386__) +# define __NR_setns 346 +# elif defined(__arc__) +# define __NR_setns 268 +# else +# error "__NR_setns is not defined" +# endif +# endif + +static inline int missing_setns(int fd, int nstype) { +# ifdef __NR_setns + return syscall(__NR_setns, fd, nstype); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define setns missing_setns +#endif + +/* ======================================================================= */ + +static inline pid_t raw_getpid(void) { +#if defined(__alpha__) + return (pid_t) syscall(__NR_getxpid); +#else + return (pid_t) syscall(__NR_getpid); +#endif +} + +/* ======================================================================= */ + +#if !HAVE_RENAMEAT2 +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_renameat2 && __NR_renameat2 >= 0) +# if defined __NR_renameat2 +# undef __NR_renameat2 +# endif +# if defined __x86_64__ +# define __NR_renameat2 316 +# elif defined __arm__ +# define __NR_renameat2 382 +# elif defined __aarch64__ +# define __NR_renameat2 276 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_renameat2 4351 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_renameat2 6315 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_renameat2 5311 +# endif +# elif defined __i386__ +# define __NR_renameat2 353 +# elif defined __powerpc64__ +# define __NR_renameat2 357 +# elif defined __s390__ || defined __s390x__ +# define __NR_renameat2 347 +# elif defined __arc__ +# define __NR_renameat2 276 +# else +# warning "__NR_renameat2 unknown for your architecture" +# endif +# endif + +static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { +# ifdef __NR_renameat2 + return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define renameat2 missing_renameat2 +#endif + +/* ======================================================================= */ + +#if !HAVE_KCMP +static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { +# if defined __NR_kcmp && __NR_kcmp >= 0 + return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define kcmp missing_kcmp +#endif + +/* ======================================================================= */ + +#if !HAVE_KEYCTL +static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { +# if defined __NR_keyctl && __NR_keyctl >= 0 + return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); +# else + errno = ENOSYS; + return -1; +# endif + +# define keyctl missing_keyctl +} + +static inline key_serial_t missing_add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) { +# if defined __NR_add_key && __NR_add_key >= 0 + return syscall(__NR_add_key, type, description, payload, plen, ringid); +# else + errno = ENOSYS; + return -1; +# endif + +# define add_key missing_add_key +} + +static inline key_serial_t missing_request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) { +# if defined __NR_request_key && __NR_request_key >= 0 + return syscall(__NR_request_key, type, description, callout_info, destringid); +# else + errno = ENOSYS; + return -1; +# endif + +# define request_key missing_request_key +} +#endif + +/* ======================================================================= */ + +#if !HAVE_COPY_FILE_RANGE +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_copy_file_range && __NR_copy_file_range >= 0) +# if defined __NR_copy_file_range +# undef __NR_copy_file_range +# endif +# if defined(__x86_64__) +# define __NR_copy_file_range 326 +# elif defined(__i386__) +# define __NR_copy_file_range 377 +# elif defined __s390__ +# define __NR_copy_file_range 375 +# elif defined __arm__ +# define __NR_copy_file_range 391 +# elif defined __aarch64__ +# define __NR_copy_file_range 285 +# elif defined __powerpc__ +# define __NR_copy_file_range 379 +# elif defined __arc__ +# define __NR_copy_file_range 285 +# else +# warning "__NR_copy_file_range not defined for your architecture" +# endif +# endif + +static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, + int fd_out, loff_t *off_out, + size_t len, + unsigned int flags) { +# ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define copy_file_range missing_copy_file_range +#endif + +/* ======================================================================= */ + +#if !HAVE_BPF +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_bpf && __NR_bpf >= 0) +# if defined __NR_bpf +# undef __NR_bpf +# endif +# if defined __i386__ +# define __NR_bpf 357 +# elif defined __x86_64__ +# define __NR_bpf 321 +# elif defined __aarch64__ +# define __NR_bpf 280 +# elif defined __arm__ +# define __NR_bpf 386 +# elif defined __sparc__ +# define __NR_bpf 349 +# elif defined __s390__ +# define __NR_bpf 351 +# elif defined __tilegx__ +# define __NR_bpf 280 +# else +# warning "__NR_bpf not defined for your architecture" +# endif +# endif + +union bpf_attr; + +static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) { +#ifdef __NR_bpf + return (int) syscall(__NR_bpf, cmd, attr, size); +#else + errno = ENOSYS; + return -1; +#endif +} + +# define bpf missing_bpf +#endif + +/* ======================================================================= */ + +#ifndef __IGNORE_pkey_mprotect +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_pkey_mprotect && __NR_pkey_mprotect >= 0) +# if defined __NR_pkey_mprotect +# undef __NR_pkey_mprotect +# endif +# if defined __i386__ +# define __NR_pkey_mprotect 380 +# elif defined __x86_64__ +# define __NR_pkey_mprotect 329 +# elif defined __arm__ +# define __NR_pkey_mprotect 394 +# elif defined __aarch64__ +# define __NR_pkey_mprotect 394 +# elif defined __powerpc__ +# define __NR_pkey_mprotect 386 +# elif defined __s390__ +# define __NR_pkey_mprotect 384 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_pkey_mprotect 4363 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_pkey_mprotect 6327 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_pkey_mprotect 5323 +# endif +# else +# warning "__NR_pkey_mprotect not defined for your architecture" +# endif +# endif +#endif + +/* ======================================================================= */ + +#if !HAVE_STATX +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_statx && __NR_statx >= 0) +# if defined __NR_statx +# undef __NR_statx +# endif +# if defined __aarch64__ || defined __arm__ +# define __NR_statx 397 +# elif defined __alpha__ +# define __NR_statx 522 +# elif defined __i386__ || defined __powerpc64__ +# define __NR_statx 383 +# elif defined __sparc__ +# define __NR_statx 360 +# elif defined __x86_64__ +# define __NR_statx 332 +# else +# warning "__NR_statx not defined for your architecture" +# endif +# endif + +struct statx; +#endif + +/* This typedef is supposed to be always defined. */ +typedef struct statx struct_statx; + +#if !HAVE_STATX +static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) { +# ifdef __NR_statx + return syscall(__NR_statx, dfd, filename, flags, mask, buffer); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define statx missing_statx +#endif + +#if !HAVE_SET_MEMPOLICY + +enum { + MPOL_DEFAULT, + MPOL_PREFERRED, + MPOL_BIND, + MPOL_INTERLEAVE, + MPOL_LOCAL, +}; + +static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask, + unsigned long maxnode) { + long i; +# if defined __NR_set_mempolicy && __NR_set_mempolicy >= 0 + i = syscall(__NR_set_mempolicy, mode, nodemask, maxnode); +# else + errno = ENOSYS; + i = -1; +# endif + return i; +} + +# define set_mempolicy missing_set_mempolicy +#endif + +#if !HAVE_GET_MEMPOLICY +static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask, + unsigned long maxnode, void *addr, + unsigned long flags) { + long i; +# ifdef __NR_get_mempolicy + i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags); +# else + errno = ENOSYS; + i = -1; +# endif + return i; +} + +#define get_mempolicy missing_get_mempolicy +#endif + +#endif /* NM_IGNORED */ + +#if !HAVE_PIDFD_OPEN +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_pidfd_open && __NR_pidfd_open >= 0) +# if defined __NR_pidfd_open +# undef __NR_pidfd_open +# endif +# define __NR_pidfd_open 434 +#endif +static inline int pidfd_open(pid_t pid, unsigned flags) { +#ifdef __NR_pidfd_open + return syscall(__NR_pidfd_open, pid, flags); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif + +#if !HAVE_PIDFD_SEND_SIGNAL +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +# if ! (defined __NR_pidfd_send_signal && __NR_pidfd_send_signal >= 0) +# if defined __NR_pidfd_send_signal +# undef __NR_pidfd_send_signal +# endif +# define __NR_pidfd_send_signal 424 +#endif +static inline int pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) { +#ifdef __NR_pidfd_open + return syscall(__NR_pidfd_send_signal, fd, sig, info, flags); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif + +#if !HAVE_RT_SIGQUEUEINFO +static inline int rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) { + return syscall(__NR_rt_sigqueueinfo, tgid, sig, info); +} +#endif diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c index 96cc43a2..475a06cc 100644 --- a/shared/systemd/src/basic/parse-util.c +++ b/shared/systemd/src/basic/parse-util.c @@ -82,11 +82,10 @@ int parse_mode(const char *s, mode_t *ret) { return 0; } -int parse_ifindex(const char *s, int *ret) { +int parse_ifindex(const char *s) { int ifi, r; assert(s); - assert(ret); r = safe_atoi(s, &ifi); if (r < 0) @@ -94,26 +93,7 @@ int parse_ifindex(const char *s, int *ret) { if (ifi <= 0) return -EINVAL; - *ret = ifi; - return 0; -} - -int parse_ifindex_or_ifname(const char *s, int *ret) { - int r; - - assert(s); - assert(ret); - - r = parse_ifindex(s, ret); - if (r >= 0) - return r; - - r = (int) if_nametoindex(s); - if (r <= 0) - return -errno; - - *ret = r; - return 0; + return ifi; } int parse_mtu(int family, const char *s, uint32_t *ret) { @@ -369,7 +349,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 @@ -393,7 +372,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; } @@ -402,7 +383,6 @@ int safe_atoi(const char *s, int *ret_i) { long l; assert(s); - assert(ret_i); errno = 0; l = strtol(s, &x, 0); @@ -413,7 +393,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; } @@ -422,7 +404,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); @@ -435,7 +416,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; } @@ -444,7 +427,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); @@ -453,7 +435,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; } @@ -462,7 +446,6 @@ int safe_atou8(const char *s, uint8_t *ret) { unsigned long l; assert(s); - assert(ret); s += strspn(s, WHITESPACE); @@ -477,7 +460,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; } @@ -511,7 +495,6 @@ int safe_atoi16(const char *s, int16_t *ret) { long l; assert(s); - assert(ret); errno = 0; l = strtol(s, &x, 0); @@ -522,7 +505,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; } @@ -533,7 +518,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) @@ -546,7 +530,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; } @@ -717,6 +703,22 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) { return 0; } +int parse_ip_prefix_length(const char *s, int *ret) { + unsigned l; + int r; + + r = safe_atou(s, &l); + if (r < 0) + return r; + + if (l > 128) + return -ERANGE; + + *ret = (int) l; + + return 0; +} + int parse_dev(const char *s, dev_t *ret) { const char *major; unsigned x, y; diff --git a/shared/systemd/src/basic/parse-util.h b/shared/systemd/src/basic/parse-util.h index 3a70b792..36d76ba5 100644 --- a/shared/systemd/src/basic/parse-util.h +++ b/shared/systemd/src/basic/parse-util.h @@ -13,8 +13,7 @@ int parse_boolean(const char *v) _pure_; int parse_dev(const char *s, dev_t *ret); int parse_pid(const char *s, pid_t* ret_pid); int parse_mode(const char *s, mode_t *ret); -int parse_ifindex(const char *s, int *ret); -int parse_ifindex_or_ifname(const char *s, int *ret); +int parse_ifindex(const char *s); int parse_mtu(int family, const char *s, uint32_t *ret); int parse_size(const char *t, uint64_t base, uint64_t *size); @@ -46,9 +45,13 @@ static inline int safe_atoux16(const char *s, uint16_t *ret) { int safe_atoi16(const char *s, int16_t *ret); -static inline int safe_atou32(const char *s, uint32_t *ret_u) { +static inline int safe_atou32_full(const char *s, unsigned base, uint32_t *ret_u) { assert_cc(sizeof(uint32_t) == sizeof(unsigned)); - return safe_atou(s, (unsigned*) ret_u); + return safe_atou_full(s, base, (unsigned*) ret_u); +} + +static inline int safe_atou32(const char *s, uint32_t *ret_u) { + return safe_atou32_full(s, 0, (unsigned*) ret_u); } static inline int safe_atoi32(const char *s, int32_t *ret_i) { @@ -113,4 +116,6 @@ int parse_nice(const char *p, int *ret); int parse_ip_port(const char *s, uint16_t *ret); int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high); +int parse_ip_prefix_length(const char *s, int *ret); + int parse_oom_score_adjust(const char *s, int *ret); diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c index 5bcc35e5..7baab4be 100644 --- a/shared/systemd/src/basic/path-util.c +++ b/shared/systemd/src/basic/path-util.c @@ -540,6 +540,7 @@ bool path_equal(const char *a, const char *b) { bool path_equal_or_files_same(const char *a, const char *b, int flags) { return path_equal(a, b) || files_same(a, b, flags) > 0; } +#endif /* NM_IGNORED */ char* path_join_internal(const char *first, ...) { char *joined, *q; @@ -599,6 +600,7 @@ char* path_join_internal(const char *first, ...) { return joined; } +#if 0 /* NM_IGNORED */ int find_binary(const char *name, char **ret) { int last_error, r; const char *p; @@ -1126,4 +1128,30 @@ bool empty_or_root(const char *root) { return root[strspn(root, "/")] == 0; } + +bool path_strv_contains(char **l, const char *path) { + char **i; + + STRV_FOREACH(i, l) + if (path_equal(*i, path)) + return true; + + return false; +} + +bool prefixed_path_strv_contains(char **l, const char *path) { + char **i, *j; + + STRV_FOREACH(i, l) { + j = *i; + if (*j == '-') + j++; + if (*j == '+') + j++; + if (path_equal(j, path)) + return true; + } + + return false; +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/path-util.h b/shared/systemd/src/basic/path-util.h index 88aef2f3..aba8ad00 100644 --- a/shared/systemd/src/basic/path-util.h +++ b/shared/systemd/src/basic/path-util.h @@ -73,17 +73,7 @@ static inline bool path_equal_ptr(const char *a, const char *b) { } /* Note: the search terminates on the first NULL item. */ -#define PATH_IN_SET(p, ...) \ - ({ \ - char **_s; \ - bool _found = false; \ - STRV_FOREACH(_s, STRV_MAKE(__VA_ARGS__)) \ - if (path_equal(p, *_s)) { \ - _found = true; \ - break; \ - } \ - _found; \ - }) +#define PATH_IN_SET(p, ...) path_strv_contains(STRV_MAKE(__VA_ARGS__), p) char* path_startswith_strv(const char *p, char **set); #define PATH_STARTSWITH_SET(p, ...) path_startswith_strv(p, STRV_MAKE(__VA_ARGS__)) @@ -183,3 +173,6 @@ bool empty_or_root(const char *root); static inline const char *empty_to_root(const char *path) { return isempty(path) ? "/" : path; } + +bool path_strv_contains(char **l, const char *path); +bool prefixed_path_strv_contains(char **l, const char *path); diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c index 14561670..2de3541c 100644 --- a/shared/systemd/src/basic/process-util.c +++ b/shared/systemd/src/basic/process-util.c @@ -26,6 +26,7 @@ #include "alloc-util.h" #include "architecture.h" #include "env-util.h" +#include "errno-util.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" @@ -44,6 +45,7 @@ #include "rlimit-util.h" #include "signal-util.h" #include "stat-util.h" +#include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "terminal-util.h" @@ -1186,6 +1188,11 @@ int must_be_root(void) { return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root."); } +static void restore_sigsetp(sigset_t **ssp) { + if (*ssp) + (void) sigprocmask(SIG_SETMASK, *ssp, NULL); +} + int safe_fork_full( const char *name, const int except_fds[], @@ -1195,7 +1202,8 @@ int safe_fork_full( pid_t original_pid, pid; sigset_t saved_ss, ss; - bool block_signals = false; + _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL; + bool block_signals = false, block_all = false; int prio, r; /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always @@ -1210,7 +1218,7 @@ int safe_fork_full( * be sure that SIGTERMs are not lost we might send to the child. */ assert_se(sigfillset(&ss) >= 0); - block_signals = true; + block_signals = block_all = true; } else if (flags & FORK_WAIT) { /* Let's block SIGCHLD at least, so that we can safely watch for the child process */ @@ -1220,28 +1228,31 @@ int safe_fork_full( block_signals = true; } - if (block_signals) + if (block_signals) { if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) return log_full_errno(prio, errno, "Failed to set signal mask: %m"); + saved_ssp = &saved_ss; + } if (flags & FORK_NEW_MOUNTNS) pid = raw_clone(SIGCHLD|CLONE_NEWNS); else pid = fork(); - if (pid < 0) { - r = -errno; - - if (block_signals) /* undo what we did above */ - (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - - return log_full_errno(prio, r, "Failed to fork: %m"); - } + if (pid < 0) + return log_full_errno(prio, errno, "Failed to fork: %m"); if (pid > 0) { /* We are in the parent process */ log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); if (flags & FORK_WAIT) { + if (block_all) { + /* undo everything except SIGCHLD */ + ss = saved_ss; + assert_se(sigaddset(&ss, SIGCHLD) >= 0); + (void) sigprocmask(SIG_SETMASK, &ss, NULL); + } + r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0)); if (r < 0) return r; @@ -1249,9 +1260,6 @@ int safe_fork_full( return -EPROTO; } - if (block_signals) /* undo what we did above */ - (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - if (ret_pid) *ret_pid = pid; @@ -1260,6 +1268,9 @@ int safe_fork_full( /* We are in the child process */ + /* Restore signal mask manually */ + saved_ssp = NULL; + if (flags & FORK_REOPEN_LOG) { /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */ log_close(); @@ -1347,6 +1358,12 @@ 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, errno, "Failed to connect stdout to stderr: %m"); + _exit(EXIT_FAILURE); + } } if (flags & FORK_RLIMIT_NOFILE_SAFE) { @@ -1498,6 +1515,94 @@ int set_oom_score_adjust(int value) { WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); } +int pidfd_get_pid(int fd, pid_t *ret) { + char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; + _cleanup_free_ char *fdinfo = NULL; + char *p; + int r; + + if (fd < 0) + return -EBADF; + + xsprintf(path, "/proc/self/fdinfo/%i", fd); + + r = read_full_file(path, &fdinfo, NULL); + if (r == -ENOENT) /* if fdinfo doesn't exist we assume the process does not exist */ + return -ESRCH; + if (r < 0) + return r; + + p = startswith(fdinfo, "Pid:"); + if (!p) { + p = strstr(fdinfo, "\nPid:"); + if (!p) + return -ENOTTY; /* not a pidfd? */ + + p += 5; + } + + p += strspn(p, WHITESPACE); + p[strcspn(p, WHITESPACE)] = 0; + + return parse_pid(p, ret); +} + +static int rlimit_to_nice(rlim_t limit) { + if (limit <= 1) + return PRIO_MAX-1; /* i.e. 19 */ + + if (limit >= -PRIO_MIN + PRIO_MAX) + return PRIO_MIN; /* i.e. -20 */ + + return PRIO_MAX - (int) limit; +} + +int setpriority_closest(int priority) { + int current, limit, saved_errno; + struct rlimit highest; + + /* Try to set requested nice level */ + if (setpriority(PRIO_PROCESS, 0, priority) >= 0) + return 1; + + /* Permission failed */ + saved_errno = -errno; + if (!ERRNO_IS_PRIVILEGE(saved_errno)) + return saved_errno; + + errno = 0; + current = getpriority(PRIO_PROCESS, 0); + if (errno != 0) + return -errno; + + if (priority == current) + return 1; + + /* Hmm, we'd expect that raising the nice level from our status quo would always work. If it doesn't, + * then the whole setpriority() system call is blocked to us, hence let's propagate the error + * right-away */ + if (priority > current) + return saved_errno; + + if (getrlimit(RLIMIT_NICE, &highest) < 0) + return -errno; + + limit = rlimit_to_nice(highest.rlim_cur); + + /* We are already less nice than limit allows us */ + if (current < limit) { + log_debug("Cannot raise nice level, permissions and the resource limit do not allow it."); + return 0; + } + + /* Push to the allowed limit */ + if (setpriority(PRIO_PROCESS, 0, limit) < 0) + return -errno; + + log_debug("Cannot set requested nice level (%i), used next best (%i).", priority, limit); + return 0; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h index 66853c6e..7b70c9f3 100644 --- a/shared/systemd/src/basic/process-util.h +++ b/shared/systemd/src/basic/process-util.h @@ -149,16 +149,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); @@ -173,7 +174,6 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid int set_oom_score_adjust(int value); -#if SIZEOF_PID_T == 4 /* The highest possibly (theoretic) pid_t value on this architecture. */ #define PID_T_MAX ((pid_t) INT32_MAX) /* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value @@ -183,12 +183,6 @@ int set_oom_score_adjust(int value); * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at * least to define them here too. */ #define TASKS_MAX 4194303U -#elif SIZEOF_PID_T == 2 -#define PID_T_MAX ((pid_t) INT16_MAX) -#define TASKS_MAX 32767U -#else -#error "Unknown pid_t size" -#endif assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX); @@ -199,3 +193,7 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX); (pid) = 0; \ _pid_; \ }) + +int pidfd_get_pid(int fd, pid_t *ret); + +int setpriority_closest(int priority); diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c index 86917ca3..1a294944 100644 --- a/shared/systemd/src/basic/random-util.c +++ b/shared/systemd/src/basic/random-util.c @@ -9,6 +9,7 @@ #include <elf.h> #include <errno.h> #include <fcntl.h> +#include <pthread.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> @@ -30,6 +31,8 @@ #include "siphash24.h" #include "time-util.h" +static bool srand_called = false; + int rdrand(unsigned long *ret) { /* So, you are a "security researcher", and you wonder why we bother with using raw RDRAND here, @@ -281,8 +284,12 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { return loop_read_exact(fd, p, n, true); } +static void clear_srand_initialization(void) { + srand_called = false; +} + void initialize_srand(void) { - static bool srand_called = false; + static bool pthread_atfork_registered = false; unsigned x; #if HAVE_SYS_AUXV_H const void *auxv; @@ -318,6 +325,11 @@ void initialize_srand(void) { srand(x); srand_called = true; + + if (!pthread_atfork_registered) { + (void) pthread_atfork(NULL, NULL, clear_srand_initialization); + pthread_atfork_registered = true; + } } /* INT_MAX gives us only 31 bits, so use 24 out of that. */ diff --git a/shared/systemd/src/basic/signal-util.c b/shared/systemd/src/basic/signal-util.c new file mode 100644 index 00000000..a4b8163c --- /dev/null +++ b/shared/systemd/src/basic/signal-util.c @@ -0,0 +1,308 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "nm-sd-adapt-shared.h" + +#include <errno.h> +#include <stdarg.h> + +#include "macro.h" +#include "parse-util.h" +#include "signal-util.h" +#include "stdio-util.h" +#include "string-table.h" +#include "string-util.h" + +#if 0 /* NM_IGNORED */ +int reset_all_signal_handlers(void) { + static const struct sigaction sa = { + .sa_handler = SIG_DFL, + .sa_flags = SA_RESTART, + }; + int sig, r = 0; + + for (sig = 1; sig < _NSIG; sig++) { + + /* These two cannot be caught... */ + if (IN_SET(sig, SIGKILL, SIGSTOP)) + continue; + + /* On Linux the first two RT signals are reserved by + * glibc, and sigaction() will return EINVAL for them. */ + if (sigaction(sig, &sa, NULL) < 0) + if (errno != EINVAL && r >= 0) + r = -errno; + } + + return r; +} + +int reset_signal_mask(void) { + sigset_t ss; + + if (sigemptyset(&ss) < 0) + return -errno; + + if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) + return -errno; + + return 0; +} + +static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) { + int r = 0; + + /* negative signal ends the list. 0 signal is skipped. */ + + if (sig < 0) + return 0; + + if (sig > 0) { + if (sigaction(sig, sa, NULL) < 0) + r = -errno; + } + + while ((sig = va_arg(ap, int)) >= 0) { + + if (sig == 0) + continue; + + if (sigaction(sig, sa, NULL) < 0) { + if (r >= 0) + r = -errno; + } + } + + return r; +} + +int sigaction_many(const struct sigaction *sa, ...) { + va_list ap; + int r; + + va_start(ap, sa); + r = sigaction_many_ap(sa, 0, ap); + va_end(ap); + + return r; +} + +int ignore_signals(int sig, ...) { + + static const struct sigaction sa = { + .sa_handler = SIG_IGN, + .sa_flags = SA_RESTART, + }; + + va_list ap; + int r; + + va_start(ap, sig); + r = sigaction_many_ap(&sa, sig, ap); + va_end(ap); + + return r; +} + +int default_signals(int sig, ...) { + + static const struct sigaction sa = { + .sa_handler = SIG_DFL, + .sa_flags = SA_RESTART, + }; + + va_list ap; + int r; + + va_start(ap, sig); + r = sigaction_many_ap(&sa, sig, ap); + va_end(ap); + + return r; +} + +static int sigset_add_many_ap(sigset_t *ss, va_list ap) { + int sig, r = 0; + + assert(ss); + + while ((sig = va_arg(ap, int)) >= 0) { + + if (sig == 0) + continue; + + if (sigaddset(ss, sig) < 0) { + if (r >= 0) + r = -errno; + } + } + + return r; +} + +int sigset_add_many(sigset_t *ss, ...) { + va_list ap; + int r; + + va_start(ap, ss); + r = sigset_add_many_ap(ss, ap); + va_end(ap); + + return r; +} + +int sigprocmask_many(int how, sigset_t *old, ...) { + va_list ap; + sigset_t ss; + int r; + + if (sigemptyset(&ss) < 0) + return -errno; + + va_start(ap, old); + r = sigset_add_many_ap(&ss, ap); + va_end(ap); + + if (r < 0) + return r; + + if (sigprocmask(how, &ss, old) < 0) + return -errno; + + return 0; +} + +static const char *const __signal_table[] = { + [SIGHUP] = "HUP", + [SIGINT] = "INT", + [SIGQUIT] = "QUIT", + [SIGILL] = "ILL", + [SIGTRAP] = "TRAP", + [SIGABRT] = "ABRT", + [SIGBUS] = "BUS", + [SIGFPE] = "FPE", + [SIGKILL] = "KILL", + [SIGUSR1] = "USR1", + [SIGSEGV] = "SEGV", + [SIGUSR2] = "USR2", + [SIGPIPE] = "PIPE", + [SIGALRM] = "ALRM", + [SIGTERM] = "TERM", +#ifdef SIGSTKFLT + [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */ +#endif + [SIGCHLD] = "CHLD", + [SIGCONT] = "CONT", + [SIGSTOP] = "STOP", + [SIGTSTP] = "TSTP", + [SIGTTIN] = "TTIN", + [SIGTTOU] = "TTOU", + [SIGURG] = "URG", + [SIGXCPU] = "XCPU", + [SIGXFSZ] = "XFSZ", + [SIGVTALRM] = "VTALRM", + [SIGPROF] = "PROF", + [SIGWINCH] = "WINCH", + [SIGIO] = "IO", + [SIGPWR] = "PWR", + [SIGSYS] = "SYS" +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); + +const char *signal_to_string(int signo) { + static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int) + 1]; + const char *name; + + name = __signal_to_string(signo); + if (name) + return name; + + if (signo >= SIGRTMIN && signo <= SIGRTMAX) + xsprintf(buf, "RTMIN+%d", signo - SIGRTMIN); + else + xsprintf(buf, "%d", signo); + + return buf; +} + +int signal_from_string(const char *s) { + const char *p; + int signo, r; + + /* Check that the input is a signal number. */ + if (safe_atoi(s, &signo) >= 0) { + if (SIGNAL_VALID(signo)) + return signo; + else + return -ERANGE; + } + + /* Drop "SIG" prefix. */ + if (startswith(s, "SIG")) + s += 3; + + /* Check that the input is a signal name. */ + signo = __signal_from_string(s); + if (signo > 0) + return signo; + + /* Check that the input is RTMIN or + * RTMIN+n (0 <= n <= SIGRTMAX-SIGRTMIN). */ + p = startswith(s, "RTMIN"); + if (p) { + if (*p == '\0') + return SIGRTMIN; + if (*p != '+') + return -EINVAL; + + r = safe_atoi(p, &signo); + if (r < 0) + return r; + + if (signo < 0 || signo > SIGRTMAX - SIGRTMIN) + return -ERANGE; + + return signo + SIGRTMIN; + } + + /* Check that the input is RTMAX or + * RTMAX-n (0 <= n <= SIGRTMAX-SIGRTMIN). */ + p = startswith(s, "RTMAX"); + if (p) { + if (*p == '\0') + return SIGRTMAX; + if (*p != '-') + return -EINVAL; + + r = safe_atoi(p, &signo); + if (r < 0) + return r; + + if (signo > 0 || signo < SIGRTMIN - SIGRTMAX) + return -ERANGE; + + return signo + SIGRTMAX; + } + + return -EINVAL; +} + +void nop_signal_handler(int sig) { + /* nothing here */ +} +#endif /* NM_IGNORED */ + +int signal_is_blocked(int sig) { + sigset_t ss; + int r; + + r = pthread_sigmask(SIG_SETMASK, NULL, &ss); + if (r != 0) + return -r; + + r = sigismember(&ss, sig); + if (r < 0) + return -errno; + + return r; +} diff --git a/shared/systemd/src/basic/signal-util.h b/shared/systemd/src/basic/signal-util.h index 92f2804c..3909ee34 100644 --- a/shared/systemd/src/basic/signal-util.h +++ b/shared/systemd/src/basic/signal-util.h @@ -41,3 +41,5 @@ static inline const char* signal_to_string_with_check(int n) { return signal_to_string(n); } + +int signal_is_blocked(int sig); diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c index cded4545..452b9ced 100644 --- a/shared/systemd/src/basic/socket-util.c +++ b/shared/systemd/src/basic/socket-util.c @@ -15,6 +15,9 @@ #include <stdlib.h> #include <sys/ioctl.h> #include <unistd.h> +#if 0 /* NM_IGNORED */ +#include <linux/if.h> +#endif /* NM_IGNORED */ #include "alloc-util.h" #include "errno-util.h" @@ -44,237 +47,16 @@ #endif static const char* const socket_address_type_table[] = { - [SOCK_STREAM] = "Stream", - [SOCK_DGRAM] = "Datagram", - [SOCK_RAW] = "Raw", - [SOCK_RDM] = "ReliableDatagram", + [SOCK_STREAM] = "Stream", + [SOCK_DGRAM] = "Datagram", + [SOCK_RAW] = "Raw", + [SOCK_RDM] = "ReliableDatagram", [SOCK_SEQPACKET] = "SequentialPacket", - [SOCK_DCCP] = "DatagramCongestionControl", + [SOCK_DCCP] = "DatagramCongestionControl", }; DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); -int socket_address_parse(SocketAddress *a, const char *s) { - _cleanup_free_ char *n = NULL; - char *e; - int r; - - assert(a); - assert(s); - - *a = (SocketAddress) { - .type = SOCK_STREAM, - }; - - if (*s == '[') { - uint16_t port; - - /* IPv6 in [x:.....:z]:p notation */ - - e = strchr(s+1, ']'); - if (!e) - return -EINVAL; - - n = strndup(s+1, e-s-1); - if (!n) - return -ENOMEM; - - errno = 0; - if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) - return errno_or_else(EINVAL); - - e++; - if (*e != ':') - return -EINVAL; - - e++; - r = parse_ip_port(e, &port); - if (r < 0) - return r; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16(port); - a->size = sizeof(struct sockaddr_in6); - - } else if (*s == '/') { - /* AF_UNIX socket */ - - size_t l; - - l = strlen(s); - 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; - memcpy(a->sockaddr.un.sun_path, s, l); - a->size = offsetof(struct sockaddr_un, sun_path) + l + 1; - - } else if (*s == '@') { - /* Abstract AF_UNIX socket */ - size_t l; - - l = strlen(s+1); - if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated 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; - memcpy(a->sockaddr.un.sun_path+1, s+1, l); - a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l; - - } else if (startswith(s, "vsock:")) { - /* AF_VSOCK socket in vsock:cid:port notation */ - const char *cid_start = s + STRLEN("vsock:"); - unsigned port; - - e = strchr(cid_start, ':'); - if (!e) - return -EINVAL; - - r = safe_atou(e+1, &port); - if (r < 0) - return r; - - n = strndup(cid_start, e - cid_start); - if (!n) - return -ENOMEM; - - if (!isempty(n)) { - r = safe_atou(n, &a->sockaddr.vm.svm_cid); - if (r < 0) - return r; - } else - a->sockaddr.vm.svm_cid = VMADDR_CID_ANY; - - a->sockaddr.vm.svm_family = AF_VSOCK; - a->sockaddr.vm.svm_port = port; - a->size = sizeof(struct sockaddr_vm); - - } else { - uint16_t port; - - e = strchr(s, ':'); - if (e) { - r = parse_ip_port(e + 1, &port); - if (r < 0) - return r; - - n = strndup(s, e-s); - if (!n) - return -ENOMEM; - - /* IPv4 in w.x.y.z:p notation? */ - r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr); - if (r < 0) - return -errno; - - if (r > 0) { - /* Gotcha, it's a traditional IPv4 address */ - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16(port); - a->size = sizeof(struct sockaddr_in); - } else { - unsigned idx; - - if (strlen(n) > IF_NAMESIZE-1) - return -EINVAL; - - /* Uh, our last resort, an interface name */ - idx = if_nametoindex(n); - if (idx == 0) - return -EINVAL; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16(port); - a->sockaddr.in6.sin6_scope_id = idx; - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } - } else { - - /* Just a port */ - r = parse_ip_port(s, &port); - if (r < 0) - return r; - - if (socket_ipv6_is_supported()) { - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16(port); - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } else { - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16(port); - a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; - a->size = sizeof(struct sockaddr_in); - } - } - } - - return 0; -} - -int socket_address_parse_and_warn(SocketAddress *a, const char *s) { - SocketAddress b; - int r; - - /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */ - - r = socket_address_parse(&b, s); - if (r < 0) - return r; - - if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) { - log_warning("Binding to IPv6 address not available since kernel does not support IPv6."); - return -EAFNOSUPPORT; - } - - *a = b; - return 0; -} - -int socket_address_parse_netlink(SocketAddress *a, const char *s) { - _cleanup_free_ char *word = NULL; - unsigned group = 0; - int family, r; - - assert(a); - assert(s); - - zero(*a); - a->type = SOCK_RAW; - - r = extract_first_word(&s, &word, NULL, 0); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - family = netlink_family_from_string(word); - if (family < 0) - return -EINVAL; - - if (!isempty(s)) { - r = safe_atou(s, &group); - if (r < 0) - return r; - } - - a->sockaddr.nl.nl_family = AF_NETLINK; - a->sockaddr.nl.nl_groups = group; - - a->type = SOCK_RAW; - a->size = sizeof(struct sockaddr_nl); - a->protocol = family; - - return 0; -} - int socket_address_verify(const SocketAddress *a, bool strict) { assert(a); @@ -484,32 +266,6 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { return true; } -bool socket_address_is(const SocketAddress *a, const char *s, int type) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse(&b, s) < 0) - return false; - - b.type = type; - - return socket_address_equal(a, &b); -} - -bool socket_address_is_netlink(const SocketAddress *a, const char *s) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse_netlink(&b, s) < 0) - return false; - - return socket_address_equal(a, &b); -} - const char* socket_address_get_path(const SocketAddress *a) { assert(a); @@ -912,7 +668,7 @@ static const char* const ip_tos_table[] = { DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); -bool ifname_valid(const char *p) { +bool ifname_valid_full(const char *p, bool alternative) { bool numeric = true; /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources @@ -922,8 +678,13 @@ bool ifname_valid(const char *p) { if (isempty(p)) return false; - if (strlen(p) >= IFNAMSIZ) - return false; + if (alternative) { + if (strlen(p) >= ALTIFNAMSIZ) + return false; + } else { + if (strlen(p) >= IFNAMSIZ) + return false; + } if (dot_or_dot_dot(p)) return false; diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h index a0886e0e..2596c540 100644 --- a/shared/systemd/src/basic/socket-util.h +++ b/shared/systemd/src/basic/socket-util.h @@ -43,6 +43,8 @@ union sockaddr_union { uint8_t un_buffer[sizeof(struct sockaddr_un) + 1]; }; +#define SUN_PATH_LEN (sizeof(((struct sockaddr_un){}).sun_path)) + typedef struct SocketAddress { union sockaddr_union sockaddr; @@ -70,12 +72,6 @@ typedef enum SocketAddressBindIPv6Only { const char* socket_address_type_to_string(int t) _const_; int socket_address_type_from_string(const char *s) _pure_; -int socket_address_parse(SocketAddress *a, const char *s); -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, bool strict) _pure_; - int sockaddr_un_unlink(const struct sockaddr_un *sa); static inline int socket_address_unlink(const SocketAddress *a) { @@ -96,11 +92,9 @@ int socket_address_listen( mode_t directory_mode, mode_t socket_mode, const char *label); -int make_socket_fd(int log_level, const char* address, int type, int flags); - -bool socket_address_is(const SocketAddress *a, const char *s, int type); -bool socket_address_is_netlink(const SocketAddress *a, const char *s); +int socket_address_verify(const SocketAddress *a, bool strict) _pure_; +int socket_address_print(const SocketAddress *a, char **p); bool socket_address_matches_fd(const SocketAddress *a, int fd); bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_; @@ -132,7 +126,10 @@ int fd_inc_rcvbuf(int fd, size_t n); int ip_tos_to_string_alloc(int i, char **s); int ip_tos_from_string(const char *s); -bool ifname_valid(const char *p); +bool ifname_valid_full(const char *p, bool alternative); +static inline bool ifname_valid(const char *p) { + return ifname_valid_full(p, false); +} bool address_label_valid(const char *p); int getpeercred(int fd, struct ucred *ucred); diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c index 071050f2..a895c1c3 100644 --- a/shared/systemd/src/basic/stat-util.c +++ b/shared/systemd/src/basic/stat-util.c @@ -12,6 +12,7 @@ #include "alloc-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "macro.h" #include "missing_fs.h" @@ -31,6 +32,7 @@ int is_symlink(const char *path) { return !!S_ISLNK(info.st_mode); } +#endif /* NM_IGNORED */ int is_dir(const char* path, bool follow) { struct stat st; @@ -48,6 +50,7 @@ int is_dir(const char* path, bool follow) { return !!S_ISDIR(st.st_mode); } +#if 0 /* NM_IGNORED */ int is_dir_fd(int fd) { struct stat st; @@ -80,10 +83,9 @@ int dir_is_empty_at(int dir_fd, const char *path) { if (fd < 0) return -errno; - d = fdopendir(fd); + d = take_fdopendir(&fd); if (!d) return -errno; - fd = -1; FOREACH_DIRENT(de, d, return -errno) return 0; diff --git a/shared/systemd/src/basic/string-table.h b/shared/systemd/src/basic/string-table.h index 2d3cf814..96924778 100644 --- a/shared/systemd/src/basic/string-table.h +++ b/shared/systemd/src/basic/string-table.h @@ -44,7 +44,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k char *s; \ if (i < 0 || i > max) \ return -ERANGE; \ - if (i < (type) ELEMENTSOF(name##_table)) { \ + if (i < (type) ELEMENTSOF(name##_table) && name##_table[i]) { \ s = strdup(name##_table[i]); \ if (!s) \ return -ENOMEM; \ diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c index 3d2feb18..9f15caca 100644 --- a/shared/systemd/src/basic/string-util.c +++ b/shared/systemd/src/basic/string-util.c @@ -115,7 +115,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) { bool escaped = false; int n; - for (n=0; s[n]; n++) { + for (n = 0; s[n] != '\0'; n++) { if (escaped) escaped = false; else if (s[n] == '\\') @@ -124,50 +124,62 @@ static size_t strcspn_escaped(const char *s, const char *reject) { break; } - /* if s ends in \, return index of previous char */ - return n - escaped; + return n; } /* Split a string into words. */ -const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) { +const char* split( + const char **state, + size_t *l, + const char *separator, + SplitFlags flags) { + const char *current; + assert(state); + assert(l); + + if (!separator) + separator = WHITESPACE; + current = *state; - if (!*current) { - assert(**state == '\0'); + if (*current == '\0') /* already at the end? */ return NULL; - } - current += strspn(current, separator); - if (!*current) { + current += strspn(current, separator); /* skip leading separators */ + if (*current == '\0') { /* at the end now? */ *state = current; return NULL; } - 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; + if (FLAGS_SET(flags, SPLIT_QUOTES)) { + + if (strchr(QUOTES, *current)) { + /* We are looking at a quote */ + *l = strcspn_escaped(current + 1, CHAR_TO_STR(*current)); + if (current[*l + 1] != *current || + (current[*l + 2] != 0 && !strchr(separator, current[*l + 2]))) { + /* right quote missing or garbage at the end */ + if (FLAGS_SET(flags, SPLIT_RELAX)) { + *state = current + *l + 1 + (current[*l + 1] != '\0'); + return current + 1; + } + *state = current; + return NULL; } - *state = current; - return NULL; - } - *state = current++ + *l + 2; - } else if (flags & SPLIT_QUOTES) { - *l = strcspn_escaped(current, separator); - if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) { - /* unfinished escape */ - *state = current; - return NULL; + *state = current++ + *l + 2; + + } else { + /* We are looking at a something that is not a quote */ + *l = strcspn_escaped(current, separator); + if (current[*l] && !strchr(separator, current[*l]) && !FLAGS_SET(flags, SPLIT_RELAX)) { + /* unfinished escape */ + *state = current; + return NULL; + } + *state = current + *l; } - *state = current + *l; } else { *l = strcspn(current, separator); *state = current + *l; @@ -1062,6 +1074,8 @@ bool string_is_safe(const char *p) { if (!p) return false; + /* Checks if the specified string contains no quotes or control characters */ + for (t = p; *t; t++) { if (*t > 0 && *t < ' ') /* no control characters */ return false; @@ -1072,3 +1086,133 @@ bool string_is_safe(const char *p) { return true; } + +#if 0 /* NM_IGNORED */ +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; +} + +int string_truncate_lines(const char *s, size_t n_lines, char **ret) { + const char *p = s, *e = s; + bool truncation_applied = false; + char *copy; + size_t n = 0; + + assert(s); + + /* Truncate after the specified number of lines. Returns > 0 if a truncation was applied or == 0 if + * there were fewer lines in the string anyway. Trailing newlines on input are ignored, and not + * generated either. */ + + for (;;) { + size_t k; + + k = strcspn(p, "\n"); + + if (p[k] == 0) { + if (k == 0) /* final empty line */ + break; + + if (n >= n_lines) /* above threshold */ + break; + + e = p + k; /* last line to include */ + break; + } + + assert(p[k] == '\n'); + + if (n >= n_lines) + break; + + if (k > 0) + e = p + k; + + p += k + 1; + n++; + } + + /* e points after the last character we want to keep */ + if (isempty(e)) + copy = strdup(s); + else { + if (!in_charset(e, "\n")) /* We only consider things truncated if we remove something that + * isn't a new-line or a series of them */ + truncation_applied = true; + + copy = strndup(s, e - s); + } + if (!copy) + return -ENOMEM; + + *ret = copy; + return truncation_applied; +} + +int string_extract_line(const char *s, size_t i, char **ret) { + const char *p = s; + size_t c = 0; + + /* Extract the i'nth line from the specified string. Returns > 0 if there are more lines after that, + * and == 0 if we are looking at the last line or already beyond the last line. As special + * optimization, if the first line is requested and the string only consists of one line we return + * NULL, indicating the input string should be used as is, and avoid a memory allocation for a very + * common case. */ + + for (;;) { + const char *q; + + q = strchr(p, '\n'); + if (i == c) { + /* The line we are looking for! */ + + if (q) { + char *m; + + m = strndup(p, q - p); + if (!m) + return -ENOMEM; + + *ret = m; + return !isempty(q + 1); /* more coming? */ + } else { + if (p == s) + *ret = NULL; /* Just use the input string */ + else { + char *m; + + m = strdup(p); + if (!m) + return -ENOMEM; + + *ret = m; + } + + return 0; /* The end */ + } + } + + if (!q) { + char *m; + + /* No more lines, return empty line */ + + m = strdup(""); + if (!m) + return -ENOMEM; + + *ret = m; + return 0; /* The end */ + } + + p = q + 1; + c++; + } +} +#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h index 04cc82b3..2a344b99 100644 --- a/shared/systemd/src/basic/string-util.h +++ b/shared/systemd/src/basic/string-util.h @@ -112,8 +112,10 @@ typedef enum SplitFlags { SPLIT_RELAX = 0x01 << 1, } SplitFlags; +/* Smelly. Do not use this anymore. Use extract_first_word() instead! */ const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags); +/* Similar, don't use this anymore */ #define FOREACH_WORD(word, length, s, state) \ _FOREACH_WORD(word, length, s, WHITESPACE, 0, state) @@ -278,3 +280,8 @@ static inline char* str_realloc(char **p) { return (*p = t); } + +char* string_erase(char *x); + +int string_truncate_lines(const char *s, size_t n_lines, char **ret); +int string_extract_line(const char *s, size_t i, char **ret); diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c index aa467132..be1c8325 100644 --- a/shared/systemd/src/basic/strv.c +++ b/shared/systemd/src/basic/strv.c @@ -18,8 +18,8 @@ #include "string-util.h" #include "strv.h" -char *strv_find(char **l, const char *name) { - char **i; +char *strv_find(char * const *l, const char *name) { + char * const *i; assert(name); @@ -30,8 +30,8 @@ char *strv_find(char **l, const char *name) { return NULL; } -char *strv_find_prefix(char **l, const char *name) { - char **i; +char *strv_find_prefix(char * const *l, const char *name) { + char * const *i; assert(name); @@ -42,8 +42,8 @@ char *strv_find_prefix(char **l, const char *name) { return NULL; } -char *strv_find_startswith(char **l, const char *name) { - char **i, *e; +char *strv_find_startswith(char * const *l, const char *name) { + char * const *i, *e; assert(name); @@ -59,20 +59,15 @@ char *strv_find_startswith(char **l, const char *name) { return NULL; } -void strv_clear(char **l) { +char **strv_free(char **l) { char **k; if (!l) - return; + return NULL; for (k = l; *k; k++) free(*k); - *l = NULL; -} - -char **strv_free(char **l) { - strv_clear(l); return mfree(l); } @@ -183,8 +178,8 @@ char **strv_new_internal(const char *x, ...) { return r; } -int strv_extend_strv(char ***a, char **b, bool filter_duplicates) { - char **s, **t; +int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { + char * const *s, **t; size_t p, q, i = 0, j; assert(a); @@ -195,7 +190,10 @@ int strv_extend_strv(char ***a, char **b, bool filter_duplicates) { p = strv_length(*a); q = strv_length(b); - t = reallocarray(*a, p + q + 1, sizeof(char *)); + if (p >= SIZE_MAX - q) + return -ENOMEM; + + t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *)); if (!t) return -ENOMEM; @@ -228,9 +226,9 @@ rollback: } #if 0 /* NM_IGNORED */ -int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { +int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) { + char * const *s; int r; - char **s; STRV_FOREACH(s, b) { char *v; @@ -349,9 +347,9 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract } #endif /* NM_IGNORED */ -char *strv_join_prefix(char **l, const char *separator, const char *prefix) { +char *strv_join_prefix(char * const *l, const char *separator, const char *prefix) { + char * const *s; char *r, *e; - char **s; size_t n, k, m; if (!separator) @@ -389,19 +387,18 @@ char *strv_join_prefix(char **l, const char *separator, const char *prefix) { int strv_push(char ***l, char *value) { char **c; - size_t n, m; + size_t n; if (!value) return 0; n = strv_length(*l); - /* Increase and check for overflow */ - m = n + 2; - if (m < n) + /* Check for overflow */ + if (n > SIZE_MAX-2) return -ENOMEM; - c = reallocarray(*l, m, sizeof(char*)); + c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + 2), sizeof(char*)); if (!c) return -ENOMEM; @@ -414,19 +411,19 @@ int strv_push(char ***l, char *value) { int strv_push_pair(char ***l, char *a, char *b) { char **c; - size_t n, m; + size_t n; if (!a && !b) return 0; n = strv_length(*l); - /* increase and check for overflow */ - m = n + !!a + !!b + 1; - if (m < n) + /* Check for overflow */ + if (n > SIZE_MAX-3) return -ENOMEM; - c = reallocarray(*l, m, sizeof(char*)); + /* increase and check for overflow */ + c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), sizeof(char*)); if (!c) return -ENOMEM; @@ -566,8 +563,8 @@ char **strv_uniq(char **l) { return l; } -bool strv_is_uniq(char **l) { - char **i; +bool strv_is_uniq(char * const *l) { + char * const *i; STRV_FOREACH(i, l) if (strv_find(i+1, *i)) @@ -672,7 +669,7 @@ char **strv_split_nulstr(const char *s) { } #endif /* NM_IGNORED */ -int strv_make_nulstr(char **l, char **p, size_t *q) { +int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { /* A valid nulstr with two NULs at the end will be created, but * q will be the length without the two trailing NULs. Thus the output * string is a valid nulstr and can be iterated over using NULSTR_FOREACH, @@ -682,10 +679,10 @@ int strv_make_nulstr(char **l, char **p, size_t *q) { size_t n_allocated = 0, n = 0; _cleanup_free_ char *m = NULL; - char **i; + char * const *i; - assert(p); - assert(q); + assert(ret); + assert(ret_size); STRV_FOREACH(i, l) { size_t z; @@ -709,16 +706,16 @@ int strv_make_nulstr(char **l, char **p, size_t *q) { m[n] = '\0'; assert(n > 0); - *p = m; - *q = n - 1; + *ret = m; + *ret_size = n - 1; m = NULL; return 0; } -bool strv_overlap(char **a, char **b) { - char **i; +bool strv_overlap(char * const *a, char * const *b) { + char * const *i; STRV_FOREACH(i, a) if (strv_contains(b, *i)) @@ -738,23 +735,30 @@ char **strv_sort(char **l) { } #endif /* NM_IGNORED */ -bool strv_equal(char **a, char **b) { +int strv_compare(char * const *a, char * const *b) { + int r; - if (strv_isempty(a)) - return strv_isempty(b); + if (strv_isempty(a)) { + if (strv_isempty(b)) + return 0; + else + return -1; + } if (strv_isempty(b)) - return false; + return 1; - for ( ; *a || *b; ++a, ++b) - if (!streq_ptr(*a, *b)) - return false; + for ( ; *a || *b; ++a, ++b) { + r = strcmp_ptr(*a, *b); + if (r != 0) + return r; + } - return true; + return 0; } -void strv_print(char **l) { - char **s; +void strv_print(char * const *l) { + char * const *s; STRV_FOREACH(s, l) puts(*s); @@ -808,12 +812,13 @@ char **strv_shell_escape(char **l, const char *bad) { return l; } -bool strv_fnmatch(char* const* patterns, const char *s, int flags) { - char* const* p; - - STRV_FOREACH(p, patterns) - if (fnmatch(*p, s, flags) == 0) +bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos) { + for (size_t i = 0; patterns && patterns[i]; i++) + if (fnmatch(patterns[i], s, flags) == 0) { + if (matched_pos) + *matched_pos = i; return true; + } return false; } @@ -856,8 +861,10 @@ int strv_extend_n(char ***l, const char *value, size_t n) { /* Adds the value n times to l */ k = strv_length(*l); + if (n >= SIZE_MAX - k) + return -ENOMEM; - nl = reallocarray(*l, k + n + 1, sizeof(char *)); + nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *)); if (!nl) return -ENOMEM; @@ -880,9 +887,9 @@ rollback: return -ENOMEM; } -int fputstrv(FILE *f, char **l, const char *separator, bool *space) { +int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) { bool b = false; - char **s; + char * const *s; int r; /* Like fputs(), but for strv, and with a less stupid argument order */ diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h index fbfa96a5..dd3323c2 100644 --- a/shared/systemd/src/basic/strv.h +++ b/shared/systemd/src/basic/strv.h @@ -13,9 +13,9 @@ #include "macro.h" #include "string-util.h" -char *strv_find(char **l, const char *name) _pure_; -char *strv_find_prefix(char **l, const char *name) _pure_; -char *strv_find_startswith(char **l, const char *name) _pure_; +char *strv_find(char * const *l, const char *name) _pure_; +char *strv_find_prefix(char * const *l, const char *name) _pure_; +char *strv_find_startswith(char * const *l, const char *name) _pure_; char **strv_free(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); @@ -25,13 +25,11 @@ char **strv_free_erase(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) -void strv_clear(char **l); - char **strv_copy(char * const *l); size_t strv_length(char * const *l) _pure_; -int strv_extend_strv(char ***a, char **b, bool filter_duplicates); -int strv_extend_strv_concat(char ***a, char **b, const char *suffix); +int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); +int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix); int strv_extend(char ***l, const char *value); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); int strv_extend_front(char ***l, const char *value); @@ -49,9 +47,12 @@ int strv_consume_prepend(char ***l, char *value); char **strv_remove(char **l, const char *s); char **strv_uniq(char **l); -bool strv_is_uniq(char **l); +bool strv_is_uniq(char * const *l); -bool strv_equal(char **a, char **b); +int strv_compare(char * const *a, char * const *b); +static inline bool strv_equal(char * const *a, char * const *b) { + return strv_compare(a, b) == 0; +} #define strv_contains(l, s) (!!strv_find((l), (s))) @@ -77,16 +78,26 @@ char **strv_split_newlines(const char *s); int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); -char *strv_join_prefix(char **l, const char *separator, const char *prefix); -static inline char *strv_join(char **l, const char *separator) { +char *strv_join_prefix(char * const *l, const char *separator, const char *prefix); +static inline char *strv_join(char * const *l, const char *separator) { return strv_join_prefix(l, separator, NULL); } char **strv_parse_nulstr(const char *s, size_t l); char **strv_split_nulstr(const char *s); -int strv_make_nulstr(char **l, char **p, size_t *n); +int strv_make_nulstr(char * const *l, char **p, size_t *n); + +static inline int strv_from_nulstr(char ***a, const char *nulstr) { + char **t; -bool strv_overlap(char **a, char **b) _pure_; + t = strv_split_nulstr(nulstr); + if (!t) + return -ENOMEM; + *a = t; + return 0; +} + +bool strv_overlap(char * const *a, char * const *b) _pure_; #define STRV_FOREACH(s, l) \ for ((s) = (l); (s) && *(s); (s)++) @@ -103,7 +114,7 @@ bool strv_overlap(char **a, char **b) _pure_; for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) char **strv_sort(char **l); -void strv_print(char **l); +void strv_print(char * const *l); #define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) @@ -177,12 +188,15 @@ void strv_print(char **l); char **strv_reverse(char **l); char **strv_shell_escape(char **l, const char *bad); -bool strv_fnmatch(char* const* patterns, const char *s, int flags); +bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos); +static inline bool strv_fnmatch(char* const* patterns, const char *s) { + return strv_fnmatch_full(patterns, s, 0, NULL); +} static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) { assert(s); return strv_isempty(patterns) || - strv_fnmatch(patterns, s, flags); + strv_fnmatch_full(patterns, s, flags, NULL); } char ***strv_free_free(char ***l); @@ -192,7 +206,7 @@ char **strv_skip(char **l, size_t n); int strv_extend_n(char ***l, const char *value, size_t n); -int fputstrv(FILE *f, char **l, const char *separator, bool *space); +int fputstrv(FILE *f, char * const *l, const char *separator, bool *space); #define strv_free_and_replace(a, b) \ ({ \ diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c index 4411127a..71af4f21 100644 --- a/shared/systemd/src/basic/time-util.c +++ b/shared/systemd/src/basic/time-util.c @@ -1507,9 +1507,30 @@ int time_change_fd(void) { if (fd < 0) return -errno; - if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) - return -errno; + if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) >= 0) + return TAKE_FD(fd); + + /* So apparently there are systems where time_t is 64bit, but the kernel actually doesn't support + * 64bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a + * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit + * of a black magic thing though, but what can we do? + * + * We don't want this code on x86-64, hence let's conditionalize this for systems with 64bit time_t + * but where "long" is shorter than 64bit, i.e. 32bit archs. + * + * See: https://github.com/systemd/systemd/issues/14362 */ + +#if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX + if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EOVERFLOW) { + static const struct itimerspec its32 = { + .it_value.tv_sec = INT32_MAX, + }; + + if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its32, NULL) >= 0) + return TAKE_FD(fd); + } +#endif - return TAKE_FD(fd); + return -errno; } #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/tmpfile-util.c b/shared/systemd/src/basic/tmpfile-util.c index d8a689e0..8bbd12b0 100644 --- a/shared/systemd/src/basic/tmpfile-util.c +++ b/shared/systemd/src/basic/tmpfile-util.c @@ -21,50 +21,58 @@ #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); + r = take_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; + 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; + int fd = -1; /* avoid false maybe-uninitialized warning */ assert(pattern); - fd = mkostemp(pattern, O_CLOEXEC); + RUN_WITH_UMASK(0077) + fd = mkostemp(pattern, O_CLOEXEC); if (fd < 0) return -errno; @@ -73,18 +81,16 @@ int mkostemp_safe(char *pattern) { #if 0 /* NM_IGNORED */ int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { - int fd; + _cleanup_close_ int fd = -1; FILE *f; fd = mkostemp_safe(pattern); if (fd < 0) return fd; - f = fdopen(fd, mode); - if (!f) { - safe_close(fd); + f = take_fdopen(&fd, mode); + if (!f) return -errno; - } *ret_f = f; return 0; diff --git a/shared/systemd/src/shared/web-util.c b/shared/systemd/src/shared/web-util.c new file mode 100644 index 00000000..f5a5362b --- /dev/null +++ b/shared/systemd/src/shared/web-util.c @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "nm-sd-adapt-shared.h" + +#include <stdbool.h> + +#include "string-util.h" +#include "strv.h" +#include "utf8.h" +#include "web-util.h" + +#if 0 /* NM_IGNORED */ +bool http_etag_is_valid(const char *etag) { + if (isempty(etag)) + return false; + + if (!endswith(etag, "\"")) + return false; + + if (!STARTSWITH_SET(etag, "\"", "W/\"")) + return false; + + return true; +} +#endif /* NM_IGNORED */ + +bool http_url_is_valid(const char *url) { + const char *p; + + if (isempty(url)) + return false; + + p = STARTSWITH_SET(url, "http://", "https://"); + if (!p) + return false; + + if (isempty(p)) + return false; + + return ascii_is_valid(p); +} + +#if 0 /* NM_IGNORED */ +bool documentation_url_is_valid(const char *url) { + const char *p; + + if (isempty(url)) + return false; + + if (http_url_is_valid(url)) + return true; + + p = STARTSWITH_SET(url, "file:/", "info:", "man:"); + if (isempty(p)) + return false; + + return ascii_is_valid(p); +} +#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/shared/web-util.h b/shared/systemd/src/shared/web-util.h new file mode 100644 index 00000000..c9e67e5c --- /dev/null +++ b/shared/systemd/src/shared/web-util.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <stdbool.h> + +#include "macro.h" + +bool http_url_is_valid(const char *url) _pure_; + +bool documentation_url_is_valid(const char *url) _pure_; + +bool http_etag_is_valid(const char *etag); |