systemd: merge branch systemd into master

This commit is contained in:
Thomas Haller 2020-03-23 17:27:34 +01:00
commit e75d62ce76
41 changed files with 1216 additions and 585 deletions

View file

@ -1819,6 +1819,7 @@ shared_systemd_libnm_systemd_shared_la_SOURCES = \
shared/systemd/nm-sd-utils-shared.c \
shared/systemd/nm-sd-utils-shared.h \
shared/systemd/sd-adapt-shared/architecture.h \
shared/systemd/sd-adapt-shared/arphrd-list.h \
shared/systemd/sd-adapt-shared/build.h \
shared/systemd/sd-adapt-shared/copy.h \
shared/systemd/sd-adapt-shared/def.h \
@ -1850,6 +1851,7 @@ shared_systemd_libnm_systemd_shared_la_SOURCES = \
shared/systemd/src/basic/alloc-util.c \
shared/systemd/src/basic/alloc-util.h \
shared/systemd/src/basic/async.h \
shared/systemd/src/basic/cgroup-util.h \
shared/systemd/src/basic/env-file.c \
shared/systemd/src/basic/env-file.h \
shared/systemd/src/basic/env-util.c \

View file

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View file

@ -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

View file

@ -0,0 +1,251 @@
/* 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_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_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);

View file

@ -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;

View file

@ -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) {

View file

@ -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++] = '\\';

View file

@ -139,16 +139,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 +173,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 +198,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 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;
}
/* 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;
}
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 +546,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 +687,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;

View file

@ -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()
@ -52,9 +54,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 +71,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);

View file

@ -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);
}

View file

@ -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)];
@ -810,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))
@ -823,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;
@ -832,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;
@ -841,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. */
@ -943,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);
@ -974,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;

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -7,6 +7,7 @@
#include <string.h>
#include <sys/types.h>
#include "alloc-util.h"
#include "macro.h"
size_t page_size(void) _pure_;
@ -88,9 +89,7 @@ static inline void* erase_and_free(void *p) {
l = malloc_usable_size(p);
explicit_bzero_safe(p, l);
free(p);
return NULL;
return mfree(p);
}
static inline void erase_and_freep(void *p) {

View file

@ -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) {
@ -723,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;

View file

@ -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);

View file

@ -1128,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 */

View file

@ -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);

View file

@ -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"
@ -1537,6 +1538,62 @@ int pidfd_get_pid(int fd, pid_t *ret) {
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",

View file

@ -174,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
@ -184,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);
@ -202,3 +195,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
})
int pidfd_get_pid(int fd, pid_t *ret);
int setpriority_closest(int priority);

View file

@ -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. */

View file

@ -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;

View file

@ -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);

View file

@ -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; \

View file

@ -1062,6 +1062,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;
@ -1083,4 +1085,122 @@ char* string_erase(char *x) {
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 */

View file

@ -280,3 +280,6 @@ static inline char* str_realloc(char **p) {
}
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);

View file

@ -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);
@ -231,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;
@ -352,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)
@ -568,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))
@ -674,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,
@ -684,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;
@ -711,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))
@ -740,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);
@ -810,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;
}
@ -884,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 */

View file

@ -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,16 @@ 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);
bool strv_overlap(char **a, char **b) _pure_;
bool strv_overlap(char * const *a, char * const *b) _pure_;
#define STRV_FOREACH(s, l) \
for ((s) = (l); (s) && *(s); (s)++)
@ -103,7 +104,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 +178,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 +196,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) \
({ \

View file

@ -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);
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 -errno;
}
#endif /* NM_IGNORED */

View file

@ -0,0 +1,143 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <grp.h>
#if ENABLE_GSHADOW
#include <gshadow.h>
#endif
#include <pwd.h>
#include <shadow.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
bool uid_is_valid(uid_t uid);
static inline bool gid_is_valid(gid_t gid) {
return uid_is_valid((uid_t) gid);
}
int parse_uid(const char *s, uid_t* ret_uid);
int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper);
static inline int parse_gid(const char *s, gid_t *ret_gid) {
return parse_uid(s, (uid_t*) ret_gid);
}
char* getlogname_malloc(void);
char* getusername_malloc(void);
typedef enum UserCredsFlags {
USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
} UserCredsFlags;
int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags);
int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags);
char* uid_to_name(uid_t uid);
char* gid_to_name(gid_t gid);
int in_gid(gid_t gid);
int in_group(const char *name);
int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **result);
int getgroups_alloc(gid_t** gids);
int get_home_dir(char **ret);
int get_shell(char **_ret);
int reset_uid_gid(void);
int take_etc_passwd_lock(const char *root);
#define UID_INVALID ((uid_t) -1)
#define GID_INVALID ((gid_t) -1)
#define UID_NOBODY ((uid_t) 65534U)
#define GID_NOBODY ((gid_t) 65534U)
#define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock"
#if 0 /* NM_ENABLED */
static inline bool uid_is_system(uid_t uid) {
return uid <= SYSTEM_UID_MAX;
}
static inline bool gid_is_system(gid_t gid) {
return gid <= SYSTEM_GID_MAX;
}
static inline bool uid_is_dynamic(uid_t uid) {
return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
}
static inline bool gid_is_dynamic(gid_t gid) {
return uid_is_dynamic((uid_t) gid);
}
static inline bool uid_is_container(uid_t uid) {
return CONTAINER_UID_BASE_MIN <= uid && uid <= CONTAINER_UID_BASE_MAX;
}
static inline bool gid_is_container(gid_t gid) {
return uid_is_container((uid_t) gid);
}
#endif /* NM_ENABLED */
/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
* NULL is special */
#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
static inline bool userns_supported(void) {
return access("/proc/self/uid_map", F_OK) >= 0;
}
bool valid_user_group_name_full(const char *u, bool strict);
bool valid_user_group_name_or_id_full(const char *u, bool strict);
static inline bool valid_user_group_name(const char *u) {
return valid_user_group_name_full(u, true);
}
static inline bool valid_user_group_name_or_id(const char *u) {
return valid_user_group_name_or_id_full(u, true);
}
static inline bool valid_user_group_name_compat(const char *u) {
return valid_user_group_name_full(u, false);
}
static inline bool valid_user_group_name_or_id_compat(const char *u) {
return valid_user_group_name_or_id_full(u, false);
}
bool valid_gecos(const char *d);
bool valid_home(const char *p);
static inline bool valid_shell(const char *p) {
/* We have the same requirements, so just piggy-back on the home check.
*
* Let's ignore /etc/shells because this is only applicable to real and
* not system users. It is also incompatible with the idea of empty /etc.
*/
return valid_home(p);
}
int maybe_setgroups(size_t size, const gid_t *list);
bool synthesize_nobody(void);
int fgetpwent_sane(FILE *stream, struct passwd **pw);
int fgetspent_sane(FILE *stream, struct spwd **sp);
int fgetgrent_sane(FILE *stream, struct group **gr);
int putpwent_sane(const struct passwd *pw, FILE *stream);
int putspent_sane(const struct spwd *sp, FILE *stream);
int putgrent_sane(const struct group *gr, FILE *stream);
#if ENABLE_GSHADOW
int fgetsgent_sane(FILE *stream, struct sgrp **sg);
int putsgent_sane(const struct sgrp *sg, FILE *stream);
#endif
bool is_nologin_shell(const char *shell);

View file

@ -10,6 +10,7 @@
#include "sd-ndisc.h"
#include "alloc-util.h"
#include "arphrd-list.h"
#include "condition.h"
#include "conf-parser.h"
#include "device-util.h"
@ -106,6 +107,18 @@ static bool net_condition_test_strv(char * const *patterns, const char *string)
return has_positive_rule ? match : true;
}
static bool net_condition_test_ifname(char * const *patterns, const char *ifname, char * const *alternative_names) {
if (net_condition_test_strv(patterns, ifname))
return true;
char * const *p;
STRV_FOREACH(p, alternative_names)
if (net_condition_test_strv(patterns, *p))
return true;
return false;
}
static int net_condition_test_property(char * const *match_property, sd_device *device) {
char * const *p;
@ -157,7 +170,29 @@ static const char *const wifi_iftype_table[NL80211_IFTYPE_MAX+1] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wifi_iftype, enum nl80211_iftype);
char *link_get_type_string(unsigned short iftype, sd_device *device) {
const char *t, *devtype;
char *p;
if (device &&
sd_device_get_devtype(device, &devtype) >= 0 &&
!isempty(devtype))
return strdup(devtype);
t = arphrd_to_name(iftype);
if (!t)
return NULL;
p = strdup(t);
if (!p)
return NULL;
ascii_strlower(p);
return p;
}
bool net_match_config(Set *match_mac,
Set *match_permanent_mac,
char * const *match_paths,
char * const *match_drivers,
char * const *match_types,
@ -166,20 +201,24 @@ bool net_match_config(Set *match_mac,
char * const *match_wifi_iftype,
char * const *match_ssid,
Set *match_bssid,
unsigned short iftype,
sd_device *device,
const struct ether_addr *dev_mac,
const struct ether_addr *dev_permanent_mac,
const char *dev_name,
char * const *alternative_names,
enum nl80211_iftype wifi_iftype,
const char *ssid,
const struct ether_addr *bssid) {
const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str;
const char *dev_path = NULL, *dev_driver = NULL, *mac_str;
_cleanup_free_ char *dev_type;
dev_type = link_get_type_string(iftype, device);
if (device) {
(void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
(void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
(void) sd_device_get_devtype(device, &dev_type);
if (!dev_name)
(void) sd_device_get_sysname(device, &dev_name);
if (!dev_mac &&
@ -190,6 +229,12 @@ bool net_match_config(Set *match_mac,
if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac)))
return false;
if (match_permanent_mac &&
(!dev_permanent_mac ||
ether_addr_is_null(dev_permanent_mac) ||
!set_contains(match_permanent_mac, dev_permanent_mac)))
return false;
if (!net_condition_test_strv(match_paths, dev_path))
return false;
@ -199,7 +244,7 @@ bool net_match_config(Set *match_mac,
if (!net_condition_test_strv(match_types, dev_type))
return false;
if (!net_condition_test_strv(match_names, dev_name))
if (!net_condition_test_ifname(match_names, dev_name, alternative_names))
return false;
if (!net_condition_test_property(match_property, device))
@ -352,7 +397,7 @@ int config_parse_match_ifnames(
return 0;
}
if (!ifname_valid(word)) {
if (!ifname_valid_full(word, ltype)) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Interface name is not valid or too long, ignoring assignment: %s", word);
continue;

View file

@ -17,6 +17,7 @@
#if 0 /* NM_IGNORED */
bool net_match_config(Set *match_mac,
Set *match_permanent_mac,
char * const *match_path,
char * const *match_driver,
char * const *match_type,
@ -25,9 +26,12 @@ bool net_match_config(Set *match_mac,
char * const *match_wifi_iftype,
char * const *match_ssid,
Set *match_bssid,
unsigned short iftype,
sd_device *device,
const struct ether_addr *dev_mac,
const struct ether_addr *dev_permanent_mac,
const char *dev_name,
char * const *alternative_names,
enum nl80211_iftype wifi_iftype,
const char *ssid,
const struct ether_addr *bssid);

View file

@ -91,7 +91,8 @@ struct sd_dhcp_client {
usec_t start_time;
uint64_t attempt;
uint64_t max_attempts;
OrderedHashmap *options;
OrderedHashmap *extra_options;
OrderedHashmap *vendor_options;
usec_t request_sent;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
@ -545,17 +546,17 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt
return 0;
}
int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) {
int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) {
int r;
assert_return(client, -EINVAL);
assert_return(v, -EINVAL);
r = ordered_hashmap_ensure_allocated(&client->options, &dhcp_option_hash_ops);
r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp_option_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(client->options, UINT_TO_PTR(v->option), v);
r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
if (r < 0)
return r;
@ -563,6 +564,25 @@ int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) {
return 0;
}
int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v) {
int r;
assert_return(client, -EINVAL);
assert_return(v, -EINVAL);
r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops);
if (r < 0)
return -ENOMEM;
r = ordered_hashmap_put(client->vendor_options, v, v);
if (r < 0)
return r;
sd_dhcp_option_ref(v);
return 1;
}
int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
assert_return(client, -EINVAL);
@ -648,7 +668,7 @@ static int client_message_init(
assert(ret);
assert(_optlen);
assert(_optoffset);
assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE));
assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE, DHCP_DECLINE));
optlen = DHCP_MIN_OPTIONS_SIZE;
size = sizeof(DHCPPacket) + optlen;
@ -889,13 +909,22 @@ static int client_send_discover(sd_dhcp_client *client) {
return r;
}
ORDERED_HASHMAP_FOREACH(j, client->options, i) {
ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
j->option, j->length, j->data);
if (r < 0)
return r;
}
if (!ordered_hashmap_isempty(client->vendor_options)) {
r = dhcp_option_append(
&discover->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_VENDOR_SPECIFIC,
ordered_hashmap_size(client->vendor_options), client->vendor_options);
if (r < 0)
return r;
}
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
@ -1971,6 +2000,48 @@ int sd_dhcp_client_send_release(sd_dhcp_client *client) {
return 0;
}
int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
assert_return(client->lease, -EUNATCH);
_cleanup_free_ DHCPPacket *release = NULL;
size_t optoffset, optlen;
int r;
r = client_message_init(client, &release, DHCP_DECLINE, &optlen, &optoffset);
if (r < 0)
return r;
release->dhcp.ciaddr = client->lease->address;
memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
r = dhcp_network_send_udp_socket(client->fd,
client->lease->server_address,
DHCP_PORT_SERVER,
&release->dhcp,
sizeof(DHCPMessage) + optoffset);
if (r < 0)
return r;
log_dhcp_client(client, "DECLINE");
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
if (client->state != DHCP_STATE_STOPPED) {
r = sd_dhcp_client_start(client);
if (r < 0)
return r;
}
return 0;
}
int sd_dhcp_client_stop(sd_dhcp_client *client) {
DHCP_CLIENT_DONT_DESTROY(client);
@ -2036,7 +2107,8 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
free(client->hostname);
free(client->vendor_class_identifier);
client->user_class = strv_free(client->user_class);
ordered_hashmap_free(client->options);
ordered_hashmap_free(client->extra_options);
ordered_hashmap_free(client->vendor_options);
return mfree(client);
}

View file

@ -681,8 +681,7 @@ static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void
}
static usec_t client_timeout_compute_random(usec_t val) {
return val - val / 10 +
(random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC;
}
static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
@ -692,7 +691,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
usec_t max_retransmit_duration = 0;
uint8_t max_retransmit_count = 0;
char time_string[FORMAT_TIMESPAN_MAX];
uint32_t expire = 0;
assert(s);
assert(client);
@ -741,8 +739,9 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
max_retransmit_time = DHCP6_REB_MAX_RT;
if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
&expire);
uint32_t expire = 0;
r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire);
if (r < 0) {
client_stop(client, r);
return 0;
@ -757,7 +756,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
return 0;
}
if (max_retransmit_count &&
if (max_retransmit_count > 0 &&
client->retransmit_count >= max_retransmit_count) {
client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
return 0;
@ -771,7 +770,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
if (r >= 0)
client->retransmit_count++;
if (!client->retransmit_time) {
if (client->retransmit_time == 0) {
client->retransmit_time =
client_timeout_compute_random(init_retransmit_time);
@ -779,7 +778,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
client->retransmit_time += init_retransmit_time / 10;
} else {
if (max_retransmit_time &&
if (max_retransmit_time > 0 &&
client->retransmit_time > max_retransmit_time / 2)
client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
else
@ -797,7 +796,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
if (r < 0)
goto error;
if (max_retransmit_duration && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
max_retransmit_duration / USEC_PER_SEC);

View file

@ -117,6 +117,9 @@ struct sd_event {
unsigned n_sources;
struct epoll_event *event_queue;
size_t event_queue_allocated;
LIST_HEAD(sd_event_source, sources);
usec_t last_run, last_log;
@ -288,6 +291,8 @@ static sd_event *event_free(sd_event *e) {
hashmap_free(e->child_sources);
set_free(e->post_sources);
free(e->event_queue);
return mfree(e);
}
@ -388,22 +393,20 @@ static int source_io_register(
int enabled,
uint32_t events) {
struct epoll_event ev;
int r;
assert(s);
assert(s->type == SOURCE_IO);
assert(enabled != SD_EVENT_OFF);
ev = (struct epoll_event) {
struct epoll_event ev = {
.events = events | (enabled == SD_EVENT_ONESHOT ? EPOLLONESHOT : 0),
.data.ptr = s,
};
int r;
if (s->io.registered)
r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->io.fd, &ev);
else
r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_ADD, s->io.fd, &ev);
r = epoll_ctl(s->event->epoll_fd,
s->io.registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD,
s->io.fd,
&ev);
if (r < 0)
return -errno;
@ -438,9 +441,7 @@ static int source_child_pidfd_register(sd_event_source *s, int enabled) {
assert(enabled != SD_EVENT_OFF);
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
struct epoll_event ev;
ev = (struct epoll_event) {
struct epoll_event ev = {
.events = EPOLLIN | (enabled == SD_EVENT_ONESHOT ? EPOLLONESHOT : 0),
.data.ptr = s,
};
@ -546,7 +547,6 @@ static int event_make_signal_data(
int sig,
struct signal_data **ret) {
struct epoll_event ev;
struct signal_data *d;
bool added = false;
sigset_t ss_copy;
@ -613,7 +613,7 @@ static int event_make_signal_data(
d->fd = fd_move_above_stdio(r);
ev = (struct epoll_event) {
struct epoll_event ev = {
.events = EPOLLIN,
.data.ptr = d,
};
@ -825,9 +825,7 @@ static void source_disconnect(sd_event_source *s) {
if (s->prepare)
prioq_remove(s->event->prepare, s, &s->prepare_index);
event = s->event;
s->event = NULL;
event = TAKE_PTR(s->event);
LIST_REMOVE(sources, event->sources, s);
event->n_sources--;
@ -1041,33 +1039,31 @@ static int event_setup_timer_fd(
struct clock_data *d,
clockid_t clock) {
struct epoll_event ev;
int r, fd;
assert(e);
assert(d);
if (_likely_(d->fd >= 0))
return 0;
_cleanup_close_ int fd = -1;
int r;
fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC);
if (fd < 0)
return -errno;
fd = fd_move_above_stdio(fd);
ev = (struct epoll_event) {
struct epoll_event ev = {
.events = EPOLLIN,
.data.ptr = d,
};
r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev);
if (r < 0) {
safe_close(fd);
if (r < 0)
return -errno;
}
d->fd = fd;
d->fd = TAKE_FD(fd);
return 0;
}
@ -1553,7 +1549,6 @@ static int event_make_inotify_data(
_cleanup_close_ int fd = -1;
struct inotify_data *d;
struct epoll_event ev;
int r;
assert(e);
@ -1592,7 +1587,7 @@ static int event_make_inotify_data(
return r;
}
ev = (struct epoll_event) {
struct epoll_event ev = {
.events = EPOLLIN,
.data.ptr = d,
};
@ -3481,8 +3476,7 @@ pending:
}
_public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
struct epoll_event *ev_queue;
unsigned ev_queue_max;
size_t event_queue_max;
int r, m, i;
assert_return(e, -EINVAL);
@ -3496,14 +3490,15 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
return 1;
}
ev_queue_max = MAX(e->n_sources, 1u);
ev_queue = newa(struct epoll_event, ev_queue_max);
event_queue_max = MAX(e->n_sources, 1u);
if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, event_queue_max))
return -ENOMEM;
/* If we still have inotify data buffered, then query the other fds, but don't wait on it */
if (e->inotify_data_buffered)
timeout = 0;
m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max,
m = epoll_wait(e->epoll_fd, e->event_queue, event_queue_max,
timeout == (uint64_t) -1 ? -1 : (int) DIV_ROUND_UP(timeout, USEC_PER_MSEC));
if (m < 0) {
if (errno == EINTR) {
@ -3519,26 +3514,26 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
for (i = 0; i < m; i++) {
if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG))
r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL);
if (e->event_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG))
r = flush_timer(e, e->watchdog_fd, e->event_queue[i].events, NULL);
else {
WakeupType *t = ev_queue[i].data.ptr;
WakeupType *t = e->event_queue[i].data.ptr;
switch (*t) {
case WAKEUP_EVENT_SOURCE: {
sd_event_source *s = ev_queue[i].data.ptr;
sd_event_source *s = e->event_queue[i].data.ptr;
assert(s);
switch (s->type) {
case SOURCE_IO:
r = process_io(e, s, ev_queue[i].events);
r = process_io(e, s, e->event_queue[i].events);
break;
case SOURCE_CHILD:
r = process_pidfd(e, s, ev_queue[i].events);
r = process_pidfd(e, s, e->event_queue[i].events);
break;
default:
@ -3549,20 +3544,20 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
}
case WAKEUP_CLOCK_DATA: {
struct clock_data *d = ev_queue[i].data.ptr;
struct clock_data *d = e->event_queue[i].data.ptr;
assert(d);
r = flush_timer(e, d->fd, ev_queue[i].events, &d->next);
r = flush_timer(e, d->fd, e->event_queue[i].events, &d->next);
break;
}
case WAKEUP_SIGNAL_DATA:
r = process_signal(e, ev_queue[i].data.ptr, ev_queue[i].events);
r = process_signal(e, e->event_queue[i].data.ptr, e->event_queue[i].events);
break;
case WAKEUP_INOTIFY_DATA:
r = event_inotify_data_read(e, ev_queue[i].data.ptr, ev_queue[i].events);
r = event_inotify_data_read(e, e->event_queue[i].data.ptr, e->event_queue[i].events);
break;
default:
@ -3846,8 +3841,6 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
return e->watchdog;
if (b) {
struct epoll_event ev;
r = sd_watchdog_enabled(false, &e->watchdog_period);
if (r <= 0)
return r;
@ -3864,7 +3857,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
if (r < 0)
goto fail;
ev = (struct epoll_event) {
struct epoll_event ev = {
.events = EPOLLIN,
.data.ptr = INT_TO_PTR(SOURCE_WATCHDOG),
};

View file

@ -195,5 +195,18 @@ int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
return memcmp(a, b, 16);
}
sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
/* Stolen from generate_random_uuid() of drivers/char/random.c
* in the kernel sources */
/* Set UUID version to 4 --- truly random generation */
id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
/* Set the UUID variant to DCE */
id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
return id;
}
DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
#endif /* NM_IGNORED */

View file

@ -30,3 +30,5 @@ int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
void id128_hash_func(const sd_id128_t *p, struct siphash *state);
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
extern const struct hash_ops id128_hash_ops;
sd_id128_t id128_make_v4_uuid(sd_id128_t id);

View file

@ -255,19 +255,6 @@ _public_ int sd_id128_get_invocation(sd_id128_t *ret) {
return 0;
}
static sd_id128_t make_v4_uuid(sd_id128_t id) {
/* Stolen from generate_random_uuid() of drivers/char/random.c
* in the kernel sources */
/* Set UUID version to 4 --- truly random generation */
id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
/* Set the UUID variant to DCE */
id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
return id;
}
_public_ int sd_id128_randomize(sd_id128_t *ret) {
sd_id128_t t;
int r;
@ -284,7 +271,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
* only guarantee this for newly generated UUIDs, not for
* pre-existing ones. */
*ret = make_v4_uuid(t);
*ret = id128_make_v4_uuid(t);
return 0;
}
@ -311,7 +298,7 @@ static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret)
/* We chop off the trailing 16 bytes */
memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
*ret = make_v4_uuid(result);
*ret = id128_make_v4_uuid(result);
return 0;
}

View file

@ -45,6 +45,18 @@ typedef void (*_sd_destroy_t)(void *userdata);
# define _sd_pure_ __attribute__((__pure__))
#endif
/* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6
* it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway
* (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers
* are probably going to use something newer anyway. */
#ifndef _sd_deprecated_
# if __GNUC__ >= 6
# define _sd_deprecated_ __attribute__((__deprecated__))
# else
# define _sd_deprecated_
# endif
#endif
#ifndef _SD_STRINGIFY
# define _SD_XSTRINGIFY(x) #x
# define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x)

View file

@ -179,11 +179,13 @@ int sd_dhcp_client_set_service_type(
sd_dhcp_client *client,
int type);
int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v);
int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);
int sd_dhcp_client_stop(sd_dhcp_client *client);
int sd_dhcp_client_start(sd_dhcp_client *client);
int sd_dhcp_client_send_release(sd_dhcp_client *client);
int sd_dhcp_client_send_decline(sd_dhcp_client *client);
int sd_dhcp_client_send_renew(sd_dhcp_client *client);
sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client);