mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-25 03:40:07 +01:00
systemd: merge branch systemd into master
This commit is contained in:
commit
23e4ef5092
58 changed files with 1539 additions and 528 deletions
|
|
@ -1235,9 +1235,11 @@ src_libsystemd_nm_la_SOURCES = \
|
|||
src/systemd/sd-adapt/ioprio.h \
|
||||
src/systemd/sd-adapt/khash.h \
|
||||
src/systemd/sd-adapt/libudev.h \
|
||||
src/systemd/sd-adapt/locale-util.h \
|
||||
src/systemd/sd-adapt/memfd-util.h \
|
||||
src/systemd/sd-adapt/missing.h \
|
||||
src/systemd/sd-adapt/mkdir.h \
|
||||
src/systemd/sd-adapt/procfs-util.h \
|
||||
src/systemd/sd-adapt/raw-clone.h \
|
||||
src/systemd/sd-adapt/sd-daemon.h \
|
||||
src/systemd/sd-adapt/stat-util.h \
|
||||
|
|
|
|||
|
|
@ -125,6 +125,9 @@
|
|||
/* The size of `time_t', as computed by sizeof. */
|
||||
#mesondefine SIZEOF_TIME_T
|
||||
|
||||
/* The size of `pid_t', as computed by sizeof. */
|
||||
#mesondefine SIZEOF_PID_T
|
||||
|
||||
/* Define to 1 to use ConsoleKit2 suspend api */
|
||||
#mesondefine SUSPEND_RESUME_CONSOLEKIT
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ dnl
|
|||
AC_TYPE_PID_T
|
||||
AC_CHECK_SIZEOF(dev_t)
|
||||
AC_CHECK_SIZEOF(time_t)
|
||||
AC_CHECK_SIZEOF(pid_t)
|
||||
|
||||
AC_CHECK_DECLS([
|
||||
explicit_bzero],
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ config_h.set10('HAVE_DECL_EXPLICIT_BZERO', cc.has_function('explicit_bzero', pre
|
|||
# types
|
||||
config_h.set('SIZEOF_DEV_T', cc.sizeof('dev_t', prefix: '#include<sys/types.h>'))
|
||||
config_h.set('SIZEOF_TIME_T', cc.sizeof('time_t', prefix: '#include<sys/types.h>'))
|
||||
config_h.set('SIZEOF_PID_T', cc.sizeof('pid_t', prefix: '#include<sys/types.h>'))
|
||||
|
||||
if not cc.has_type('pid_t', prefix: '#include<sys/types.h>')
|
||||
config_h.set('pid_t', 'int')
|
||||
|
|
|
|||
|
|
@ -26,89 +26,77 @@
|
|||
/* BE */
|
||||
|
||||
static inline uint16_t unaligned_read_be16(const void *_u) {
|
||||
const uint8_t *u = _u;
|
||||
const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
|
||||
|
||||
return (((uint16_t) u[0]) << 8) |
|
||||
((uint16_t) u[1]);
|
||||
return be16toh(u->x);
|
||||
}
|
||||
|
||||
static inline uint32_t unaligned_read_be32(const void *_u) {
|
||||
const uint8_t *u = _u;
|
||||
const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
|
||||
|
||||
return (((uint32_t) unaligned_read_be16(u)) << 16) |
|
||||
((uint32_t) unaligned_read_be16(u + 2));
|
||||
return be32toh(u->x);
|
||||
}
|
||||
|
||||
static inline uint64_t unaligned_read_be64(const void *_u) {
|
||||
const uint8_t *u = _u;
|
||||
const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
|
||||
|
||||
return (((uint64_t) unaligned_read_be32(u)) << 32) |
|
||||
((uint64_t) unaligned_read_be32(u + 4));
|
||||
return be64toh(u->x);
|
||||
}
|
||||
|
||||
static inline void unaligned_write_be16(void *_u, uint16_t a) {
|
||||
uint8_t *u = _u;
|
||||
struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
|
||||
|
||||
u[0] = (uint8_t) (a >> 8);
|
||||
u[1] = (uint8_t) a;
|
||||
u->x = be16toh(a);
|
||||
}
|
||||
|
||||
static inline void unaligned_write_be32(void *_u, uint32_t a) {
|
||||
uint8_t *u = _u;
|
||||
struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
|
||||
|
||||
unaligned_write_be16(u, (uint16_t) (a >> 16));
|
||||
unaligned_write_be16(u + 2, (uint16_t) a);
|
||||
u->x = be32toh(a);
|
||||
}
|
||||
|
||||
static inline void unaligned_write_be64(void *_u, uint64_t a) {
|
||||
uint8_t *u = _u;
|
||||
struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
|
||||
|
||||
unaligned_write_be32(u, (uint32_t) (a >> 32));
|
||||
unaligned_write_be32(u + 4, (uint32_t) a);
|
||||
u->x = be64toh(a);
|
||||
}
|
||||
|
||||
/* LE */
|
||||
|
||||
static inline uint16_t unaligned_read_le16(const void *_u) {
|
||||
const uint8_t *u = _u;
|
||||
const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
|
||||
|
||||
return (((uint16_t) u[1]) << 8) |
|
||||
((uint16_t) u[0]);
|
||||
return le16toh(u->x);
|
||||
}
|
||||
|
||||
static inline uint32_t unaligned_read_le32(const void *_u) {
|
||||
const uint8_t *u = _u;
|
||||
const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
|
||||
|
||||
return (((uint32_t) unaligned_read_le16(u + 2)) << 16) |
|
||||
((uint32_t) unaligned_read_le16(u));
|
||||
return le32toh(u->x);
|
||||
}
|
||||
|
||||
static inline uint64_t unaligned_read_le64(const void *_u) {
|
||||
const uint8_t *u = _u;
|
||||
const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
|
||||
|
||||
return (((uint64_t) unaligned_read_le32(u + 4)) << 32) |
|
||||
((uint64_t) unaligned_read_le32(u));
|
||||
return le64toh(u->x);
|
||||
}
|
||||
|
||||
static inline void unaligned_write_le16(void *_u, uint16_t a) {
|
||||
uint8_t *u = _u;
|
||||
struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
|
||||
|
||||
u[0] = (uint8_t) a;
|
||||
u[1] = (uint8_t) (a >> 8);
|
||||
u->x = le16toh(a);
|
||||
}
|
||||
|
||||
static inline void unaligned_write_le32(void *_u, uint32_t a) {
|
||||
uint8_t *u = _u;
|
||||
struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
|
||||
|
||||
unaligned_write_le16(u, (uint16_t) a);
|
||||
unaligned_write_le16(u + 2, (uint16_t) (a >> 16));
|
||||
u->x = le32toh(a);
|
||||
}
|
||||
|
||||
static inline void unaligned_write_le64(void *_u, uint64_t a) {
|
||||
uint8_t *u = _u;
|
||||
struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
|
||||
|
||||
unaligned_write_le32(u, (uint32_t) a);
|
||||
unaligned_write_le32(u + 4, (uint32_t) (a >> 32));
|
||||
u->x = le64toh(a);
|
||||
}
|
||||
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
|
|
|
|||
3
src/systemd/sd-adapt/locale-util.h
Normal file
3
src/systemd/sd-adapt/locale-util.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
/* dummy header */
|
||||
|
|
@ -138,6 +138,15 @@ G_STMT_START { \
|
|||
# endif
|
||||
#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
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* work around missing uchar.h */
|
||||
|
|
@ -146,8 +155,6 @@ typedef guint32 char32_t;
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
|
||||
|
||||
static inline int
|
||||
sd_notify (int unset_environment, const char *state)
|
||||
{
|
||||
|
|
|
|||
3
src/systemd/sd-adapt/procfs-util.h
Normal file
3
src/systemd/sd-adapt/procfs-util.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
/* dummy header */
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "nm-sd-adapt.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
|
|
|||
|
|
@ -584,3 +584,40 @@ try_dev_shm_without_o_tmpfile:
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int fd_move_above_stdio(int fd) {
|
||||
int flags, copy;
|
||||
PROTECT_ERRNO;
|
||||
|
||||
/* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of
|
||||
* stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is
|
||||
* returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that
|
||||
* might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as
|
||||
* stdin/stdout/stderr of unrelated code.
|
||||
*
|
||||
* Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by
|
||||
* buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has
|
||||
* been closed before.
|
||||
*
|
||||
* This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an
|
||||
* error we simply return the original file descriptor, and we do not touch errno. */
|
||||
|
||||
if (fd < 0 || fd > 2)
|
||||
return fd;
|
||||
|
||||
flags = fcntl(fd, F_GETFD, 0);
|
||||
if (flags < 0)
|
||||
return fd;
|
||||
|
||||
if (flags & FD_CLOEXEC)
|
||||
copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
|
||||
else
|
||||
copy = fcntl(fd, F_DUPFD, 3);
|
||||
if (copy < 0)
|
||||
return fd;
|
||||
|
||||
assert(copy > 2);
|
||||
|
||||
(void) close(fd);
|
||||
return copy;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,3 +91,5 @@ int acquire_data_fd(const void *data, size_t size, unsigned flags);
|
|||
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
|
||||
#define ERRNO_IS_DISCONNECT(r) \
|
||||
IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
|
||||
|
||||
int fd_move_above_stdio(int fd);
|
||||
|
|
|
|||
|
|
@ -65,16 +65,28 @@ int write_string_stream_ts(
|
|||
WriteStringFileFlags flags,
|
||||
struct timespec *ts) {
|
||||
|
||||
bool needs_nl;
|
||||
|
||||
assert(f);
|
||||
assert(line);
|
||||
|
||||
if (ferror(f))
|
||||
return -EIO;
|
||||
|
||||
needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
|
||||
|
||||
if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
|
||||
/* If STDIO buffering was disabled, then let's append the newline character to the string itself, so
|
||||
* that the write goes out in one go, instead of two */
|
||||
|
||||
line = strjoina(line, "\n");
|
||||
needs_nl = false;
|
||||
}
|
||||
|
||||
if (fputs(line, f) == EOF)
|
||||
return -errno;
|
||||
|
||||
if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"))
|
||||
if (needs_nl)
|
||||
if (fputc('\n', f) == EOF)
|
||||
return -errno;
|
||||
|
||||
|
|
@ -919,14 +931,16 @@ int write_env_file(const char *fname, char **l) {
|
|||
}
|
||||
|
||||
int executable_is_script(const char *path, char **interpreter) {
|
||||
int r;
|
||||
_cleanup_free_ char *line = NULL;
|
||||
int len;
|
||||
size_t len;
|
||||
char *ans;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
r = read_one_line_file(path, &line);
|
||||
if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
@ -1219,8 +1233,7 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
|
|||
if (!filename_is_valid(fn))
|
||||
return -EINVAL;
|
||||
|
||||
if (!extra)
|
||||
extra = "";
|
||||
extra = strempty(extra);
|
||||
|
||||
t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
|
||||
if (!t)
|
||||
|
|
@ -1254,8 +1267,7 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
|
|||
if (!filename_is_valid(fn))
|
||||
return -EINVAL;
|
||||
|
||||
if (!extra)
|
||||
extra = "";
|
||||
extra = strempty(extra);
|
||||
|
||||
t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
|
||||
if (!t)
|
||||
|
|
@ -1295,8 +1307,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
|
|||
return r;
|
||||
}
|
||||
|
||||
if (!extra)
|
||||
extra = "";
|
||||
extra = strempty(extra);
|
||||
|
||||
t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
|
||||
if (!t)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include "mkdir.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
|
|
@ -229,49 +230,6 @@ int readlink_and_make_absolute(const char *p, char **r) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int readlink_and_canonicalize(const char *p, const char *root, char **ret) {
|
||||
char *t, *s;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
r = readlink_and_make_absolute(p, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = chase_symlinks(t, root, 0, &s);
|
||||
if (r < 0)
|
||||
/* If we can't follow up, then let's return the original string, slightly cleaned up. */
|
||||
*ret = path_kill_slashes(t);
|
||||
else {
|
||||
*ret = s;
|
||||
free(t);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
|
||||
_cleanup_free_ char *target = NULL, *t = NULL;
|
||||
const char *full;
|
||||
int r;
|
||||
|
||||
full = prefix_roota(root, path);
|
||||
r = readlink_malloc(full, &target);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t = file_in_same_dir(path, target);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = t;
|
||||
t = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
|
||||
assert(path);
|
||||
|
||||
|
|
@ -322,43 +280,60 @@ int fd_warn_permissions(const char *path, int fd) {
|
|||
}
|
||||
|
||||
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
|
||||
_cleanup_close_ int fd;
|
||||
int r;
|
||||
char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r, ret = 0;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink
|
||||
* itself which is updated, not its target
|
||||
*
|
||||
* Returns the first error we encounter, but tries to apply as much as possible. */
|
||||
|
||||
if (parents)
|
||||
mkdir_parents(path, 0755);
|
||||
(void) mkdir_parents(path, 0755);
|
||||
|
||||
fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
|
||||
IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
/* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in
|
||||
* case the path refers to an existing device or socket node, as we can open it successfully in all cases, and
|
||||
* won't trigger any driver magic or so. */
|
||||
fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (fd < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
if (mode != MODE_INVALID) {
|
||||
r = fchmod(fd, mode);
|
||||
if (r < 0)
|
||||
/* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file
|
||||
* here, and nothing else */
|
||||
fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (uid != UID_INVALID || gid != GID_INVALID) {
|
||||
r = fchown(fd, uid, gid);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
}
|
||||
/* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
|
||||
* ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
|
||||
* something fchown(), fchmod(), futimensat() don't allow. */
|
||||
xsprintf(fdpath, "/proc/self/fd/%i", fd);
|
||||
|
||||
if (mode != MODE_INVALID)
|
||||
if (chmod(fdpath, mode) < 0)
|
||||
ret = -errno;
|
||||
|
||||
if (uid_is_valid(uid) || gid_is_valid(gid))
|
||||
if (chown(fdpath, uid, gid) < 0 && ret >= 0)
|
||||
ret = -errno;
|
||||
|
||||
if (stamp != USEC_INFINITY) {
|
||||
struct timespec ts[2];
|
||||
|
||||
timespec_store(&ts[0], stamp);
|
||||
ts[1] = ts[0];
|
||||
r = futimens(fd, ts);
|
||||
r = utimensat(AT_FDCWD, fdpath, ts, 0);
|
||||
} else
|
||||
r = futimens(fd, NULL);
|
||||
if (r < 0)
|
||||
r = utimensat(AT_FDCWD, fdpath, NULL, 0);
|
||||
if (r < 0 && ret >= 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int touch(const char *path) {
|
||||
|
|
@ -600,16 +575,35 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
|
|||
return r;
|
||||
}
|
||||
|
||||
static bool safe_transition(const struct stat *a, const struct stat *b) {
|
||||
/* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
|
||||
* privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
|
||||
* making us believe we read something safe even though it isn't safe in the specific context we open it in. */
|
||||
|
||||
if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
|
||||
return true;
|
||||
|
||||
return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
|
||||
}
|
||||
|
||||
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
|
||||
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
|
||||
struct stat previous_stat;
|
||||
bool exists = true;
|
||||
char *todo;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* Either the file may be missing, or we return an fd to the final object, but both make no sense */
|
||||
if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
|
||||
return -EINVAL;
|
||||
|
||||
if (isempty(path))
|
||||
return -EINVAL;
|
||||
|
||||
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
|
||||
* symlinks relative to a root directory, instead of the root of the host.
|
||||
*
|
||||
|
|
@ -630,13 +624,23 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
* function what to do when encountering a symlink with an absolute path as directory: prefix it by the
|
||||
* specified path. */
|
||||
|
||||
/* A root directory of "/" or "" is identical to none */
|
||||
if (isempty(original_root) || path_equal(original_root, "/"))
|
||||
original_root = NULL;
|
||||
|
||||
if (original_root) {
|
||||
r = path_make_absolute_cwd(original_root, &root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (flags & CHASE_PREFIX_ROOT)
|
||||
if (flags & CHASE_PREFIX_ROOT) {
|
||||
|
||||
/* We don't support relative paths in combination with a root directory */
|
||||
if (!path_is_absolute(path))
|
||||
return -EINVAL;
|
||||
|
||||
path = prefix_roota(root, path);
|
||||
}
|
||||
}
|
||||
|
||||
r = path_make_absolute_cwd(path, &buffer);
|
||||
|
|
@ -647,6 +651,11 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (flags & CHASE_SAFE) {
|
||||
if (fstat(fd, &previous_stat) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
todo = buffer;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *first = NULL;
|
||||
|
|
@ -685,7 +694,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
/* Two dots? Then chop off the last bit of what we already found out. */
|
||||
if (path_equal(first, "/..")) {
|
||||
_cleanup_free_ char *parent = NULL;
|
||||
int fd_parent = -1;
|
||||
_cleanup_close_ int fd_parent = -1;
|
||||
|
||||
/* If we already are at the top, then going up will not change anything. This is in-line with
|
||||
* how the kernel handles this. */
|
||||
|
|
@ -708,8 +717,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
if (fd_parent < 0)
|
||||
return -errno;
|
||||
|
||||
if (flags & CHASE_SAFE) {
|
||||
if (fstat(fd_parent, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!safe_transition(&previous_stat, &st))
|
||||
return -EPERM;
|
||||
|
||||
previous_stat = st;
|
||||
}
|
||||
|
||||
safe_close(fd);
|
||||
fd = fd_parent;
|
||||
fd_parent = -1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
|
@ -742,6 +762,12 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
|
||||
if (fstat(child, &st) < 0)
|
||||
return -errno;
|
||||
if ((flags & CHASE_SAFE) &&
|
||||
!safe_transition(&previous_stat, &st))
|
||||
return -EPERM;
|
||||
|
||||
previous_stat = st;
|
||||
|
||||
if ((flags & CHASE_NO_AUTOFS) &&
|
||||
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
|
||||
return -EREMOTE;
|
||||
|
|
@ -772,6 +798,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (flags & CHASE_SAFE) {
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!safe_transition(&previous_stat, &st))
|
||||
return -EPERM;
|
||||
|
||||
previous_stat = st;
|
||||
}
|
||||
|
||||
free(done);
|
||||
|
||||
/* Note that we do not revalidate the root, we take it as is. */
|
||||
|
|
@ -828,6 +864,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
done = NULL;
|
||||
}
|
||||
|
||||
if (flags & CHASE_OPEN) {
|
||||
int q;
|
||||
|
||||
/* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
|
||||
* opening /proc/self/fd/xyz. */
|
||||
|
||||
assert(fd >= 0);
|
||||
q = fd;
|
||||
fd = -1;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
|
|
@ -845,4 +894,73 @@ int access_fd(int fd, int mode) {
|
|||
|
||||
return r;
|
||||
}
|
||||
|
||||
int unlinkat_deallocate(int fd, const char *name, int flags) {
|
||||
_cleanup_close_ int truncate_fd = -1;
|
||||
struct stat st;
|
||||
off_t l, bs;
|
||||
|
||||
/* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
|
||||
* link to it. This is useful to ensure that other processes that might have the file open for reading won't be
|
||||
* able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up
|
||||
* jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and
|
||||
* returned to the free pool.
|
||||
*
|
||||
* Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means
|
||||
* the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other
|
||||
* programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes
|
||||
* underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.)
|
||||
* However if hole punching is not implemented in the kernel or file system we'll fall back to normal file
|
||||
* truncation (🔪), as our goal of deallocating the data space trumps our goal of being nice to readers (💐).
|
||||
*
|
||||
* Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the
|
||||
* primary job – to delete the file – is accomplished. */
|
||||
|
||||
if ((flags & AT_REMOVEDIR) == 0) {
|
||||
truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
|
||||
if (truncate_fd < 0) {
|
||||
|
||||
/* If this failed because the file doesn't exist propagate the error right-away. Also,
|
||||
* AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is
|
||||
* returned when this is a directory but we are not supposed to delete those, hence propagate
|
||||
* the error right-away too. */
|
||||
if (IN_SET(errno, ENOENT, EISDIR))
|
||||
return -errno;
|
||||
|
||||
if (errno != ELOOP) /* don't complain if this is a symlink */
|
||||
log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name);
|
||||
}
|
||||
}
|
||||
|
||||
if (unlinkat(fd, name, flags) < 0)
|
||||
return -errno;
|
||||
|
||||
if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */
|
||||
return 0;
|
||||
|
||||
if (fstat(truncate_fd, &st) < 0) {
|
||||
log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0)
|
||||
return 0;
|
||||
|
||||
/* If this is a regular file, it actually took up space on disk and there are no other links it's time to
|
||||
* punch-hole/truncate this to release the disk space. */
|
||||
|
||||
bs = MAX(st.st_blksize, 512);
|
||||
l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */
|
||||
|
||||
if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0)
|
||||
return 0; /* Successfully punched a hole! 😊 */
|
||||
|
||||
/* Fall back to truncation */
|
||||
if (ftruncate(truncate_fd, 0) < 0) {
|
||||
log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "time-util.h"
|
||||
#include "util.h"
|
||||
|
||||
int unlink_noerrno(const char *path);
|
||||
|
||||
|
|
@ -40,8 +41,6 @@ int readlinkat_malloc(int fd, const char *p, char **ret);
|
|||
int readlink_malloc(const char *p, char **r);
|
||||
int readlink_value(const char *p, char **ret);
|
||||
int readlink_and_make_absolute(const char *p, char **r);
|
||||
int readlink_and_canonicalize(const char *p, const char *root, char **r);
|
||||
int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
|
||||
|
||||
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
|
||||
|
||||
|
|
@ -80,24 +79,29 @@ union inotify_event_buffer {
|
|||
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
|
||||
|
||||
enum {
|
||||
CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
|
||||
CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */
|
||||
CHASE_NO_AUTOFS = 4, /* If set, return -EREMOTE if autofs mount point found */
|
||||
CHASE_PREFIX_ROOT = 1U << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
|
||||
CHASE_NONEXISTENT = 1U << 1, /* If set, it's OK if the path doesn't actually exist. */
|
||||
CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */
|
||||
CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
|
||||
CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */
|
||||
};
|
||||
|
||||
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
|
||||
|
||||
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
|
||||
static inline void rmdir_and_free(char *p) {
|
||||
PROTECT_ERRNO;
|
||||
(void) rmdir(p);
|
||||
free(p);
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
|
||||
|
||||
static inline void unlink_and_free(char *p) {
|
||||
(void) unlink(p);
|
||||
(void) unlink_noerrno(p);
|
||||
free(p);
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
|
||||
|
||||
int access_fd(int fd, int mode);
|
||||
|
||||
int unlinkat_deallocate(int fd, const char *name, int flags);
|
||||
|
|
|
|||
|
|
@ -21,12 +21,16 @@
|
|||
|
||||
#include "nm-sd-adapt.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "hash-funcs.h"
|
||||
#include "path-util.h"
|
||||
|
||||
void string_hash_func(const void *p, struct siphash *state) {
|
||||
siphash24_compress(p, strlen(p) + 1, state);
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int string_compare_func(const void *a, const void *b) {
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
|
@ -36,6 +40,56 @@ const struct hash_ops string_hash_ops = {
|
|||
.compare = string_compare_func
|
||||
};
|
||||
|
||||
|
||||
void path_hash_func(const void *p, struct siphash *state) {
|
||||
const char *q = p;
|
||||
size_t n;
|
||||
|
||||
assert(q);
|
||||
assert(state);
|
||||
|
||||
/* Calculates a hash for a path in a way this duplicate inner slashes don't make a differences, and also
|
||||
* whether there's a trailing slash or not. This fits well with the semantics of path_compare(), which does
|
||||
* similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those
|
||||
* which begin in a slash or not) will hash differently though. */
|
||||
|
||||
n = strspn(q, "/");
|
||||
if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */
|
||||
siphash24_compress(q, 1, state);
|
||||
q += n;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* Determine length of next component */
|
||||
n = strcspn(q, "/");
|
||||
if (n == 0) /* Reached the end? */
|
||||
break;
|
||||
|
||||
/* Add this component to the hash and skip over it */
|
||||
siphash24_compress(q, n, state);
|
||||
q += n;
|
||||
|
||||
/* How many slashes follow this component? */
|
||||
n = strspn(q, "/");
|
||||
if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */
|
||||
break;
|
||||
|
||||
/* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */
|
||||
siphash24_compress(q, 1, state);
|
||||
q += n;
|
||||
}
|
||||
}
|
||||
|
||||
int path_compare_func(const void *a, const void *b) {
|
||||
return path_compare(a, b);
|
||||
}
|
||||
|
||||
const struct hash_ops path_hash_ops = {
|
||||
.hash = path_hash_func,
|
||||
.compare = path_compare_func
|
||||
};
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
void trivial_hash_func(const void *p, struct siphash *state) {
|
||||
siphash24_compress(&p, sizeof(p), state);
|
||||
}
|
||||
|
|
@ -65,6 +119,7 @@ const struct hash_ops uint64_hash_ops = {
|
|||
.compare = uint64_compare_func
|
||||
};
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
#if SIZEOF_DEV_T != 8
|
||||
void devt_hash_func(const void *p, struct siphash *state) {
|
||||
siphash24_compress(p, sizeof(dev_t), state);
|
||||
|
|
@ -82,3 +137,4 @@ const struct hash_ops devt_hash_ops = {
|
|||
.compare = devt_compare_func
|
||||
};
|
||||
#endif
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
|||
|
|
@ -36,29 +36,28 @@ void string_hash_func(const void *p, struct siphash *state);
|
|||
int string_compare_func(const void *a, const void *b) _pure_;
|
||||
extern const struct hash_ops string_hash_ops;
|
||||
|
||||
/* This will compare the passed pointers directly, and will not
|
||||
* dereference them. This is hence not useful for strings or
|
||||
* suchlike. */
|
||||
void path_hash_func(const void *p, struct siphash *state);
|
||||
int path_compare_func(const void *a, const void *b) _pure_;
|
||||
extern const struct hash_ops path_hash_ops;
|
||||
|
||||
/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
|
||||
* or suchlike. */
|
||||
void trivial_hash_func(const void *p, struct siphash *state);
|
||||
int trivial_compare_func(const void *a, const void *b) _const_;
|
||||
extern const struct hash_ops trivial_hash_ops;
|
||||
|
||||
/* 32bit values we can always just embed in the pointer itself, but
|
||||
* in order to support 32bit archs we need store 64bit values
|
||||
* indirectly, since they don't fit in a pointer. */
|
||||
/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
|
||||
* values indirectly, since they don't fit in a pointer. */
|
||||
void uint64_hash_func(const void *p, struct siphash *state);
|
||||
int uint64_compare_func(const void *a, const void *b) _pure_;
|
||||
extern const struct hash_ops uint64_hash_ops;
|
||||
|
||||
/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
|
||||
* it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
|
||||
/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on
|
||||
* 64bit archs. Yuck! */
|
||||
#if SIZEOF_DEV_T != 8
|
||||
void devt_hash_func(const void *p, struct siphash *state) _pure_;
|
||||
int devt_compare_func(const void *a, const void *b) _pure_;
|
||||
extern const struct hash_ops devt_hash_ops = {
|
||||
.hash = devt_hash_func,
|
||||
.compare = devt_compare_func
|
||||
};
|
||||
extern const struct hash_ops devt_hash_ops;
|
||||
#else
|
||||
#define devt_hash_func uint64_hash_func
|
||||
#define devt_compare_func uint64_compare_func
|
||||
|
|
|
|||
|
|
@ -231,6 +231,8 @@ struct HashmapBase {
|
|||
unsigned n_direct_entries:3; /* Number of entries in direct storage.
|
||||
* Only valid if !has_indirect. */
|
||||
bool from_pool:1; /* whether was allocated from mempool */
|
||||
bool dirty:1; /* whether dirtied since last iterated_cache_get() */
|
||||
bool cached:1; /* whether this hashmap is being cached */
|
||||
HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */
|
||||
};
|
||||
|
||||
|
|
@ -250,6 +252,17 @@ struct Set {
|
|||
struct HashmapBase b;
|
||||
};
|
||||
|
||||
typedef struct CacheMem {
|
||||
const void **ptr;
|
||||
size_t n_populated, n_allocated;
|
||||
bool active:1;
|
||||
} CacheMem;
|
||||
|
||||
struct IteratedCache {
|
||||
HashmapBase *hashmap;
|
||||
CacheMem keys, values;
|
||||
};
|
||||
|
||||
DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8);
|
||||
DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
|
||||
/* No need for a separate Set pool */
|
||||
|
|
@ -353,6 +366,11 @@ static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
|
|||
}
|
||||
#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
|
||||
|
||||
static inline void base_set_dirty(HashmapBase *h) {
|
||||
h->dirty = true;
|
||||
}
|
||||
#define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h))
|
||||
|
||||
static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
|
||||
static uint8_t current[HASH_KEY_SIZE];
|
||||
static bool current_initialized = false;
|
||||
|
|
@ -570,6 +588,7 @@ static void base_remove_entry(HashmapBase *h, unsigned idx) {
|
|||
|
||||
bucket_mark_free(h, prev);
|
||||
n_entries_dec(h);
|
||||
base_set_dirty(h);
|
||||
}
|
||||
#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
|
||||
|
||||
|
|
@ -739,6 +758,25 @@ bool set_iterate(Set *s, Iterator *i, void **value) {
|
|||
(idx != IDX_NIL); \
|
||||
(idx) = hashmap_iterate_entry((h), &(i)))
|
||||
|
||||
IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) {
|
||||
IteratedCache *cache;
|
||||
|
||||
assert(h);
|
||||
assert(!h->cached);
|
||||
|
||||
if (h->cached)
|
||||
return NULL;
|
||||
|
||||
cache = new0(IteratedCache, 1);
|
||||
if (!cache)
|
||||
return NULL;
|
||||
|
||||
cache->hashmap = h;
|
||||
h->cached = true;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
static void reset_direct_storage(HashmapBase *h) {
|
||||
const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
|
||||
void *p;
|
||||
|
|
@ -899,6 +937,8 @@ void internal_hashmap_clear(HashmapBase *h) {
|
|||
OrderedHashmap *lh = (OrderedHashmap*) h;
|
||||
lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
|
||||
}
|
||||
|
||||
base_set_dirty(h);
|
||||
}
|
||||
|
||||
void internal_hashmap_clear_free(HashmapBase *h) {
|
||||
|
|
@ -1043,6 +1083,8 @@ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
|
|||
h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
|
||||
#endif
|
||||
|
||||
base_set_dirty(h);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#define hashmap_put_boldly(h, idx, swap, may_resize) \
|
||||
|
|
@ -1279,6 +1321,8 @@ int hashmap_replace(Hashmap *h, const void *key, void *value) {
|
|||
#endif
|
||||
e->b.key = key;
|
||||
e->value = value;
|
||||
hashmap_set_dirty(h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1301,6 +1345,8 @@ int hashmap_update(Hashmap *h, const void *key, void *value) {
|
|||
|
||||
e = plain_bucket_at(h, idx);
|
||||
e->value = value;
|
||||
hashmap_set_dirty(h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1853,3 +1899,95 @@ int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags
|
|||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/* expand the cachemem if needed, return true if newly (re)activated. */
|
||||
static int cachemem_maintain(CacheMem *mem, unsigned size) {
|
||||
assert(mem);
|
||||
|
||||
if (!GREEDY_REALLOC(mem->ptr, mem->n_allocated, size)) {
|
||||
if (size > 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!mem->active) {
|
||||
mem->active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) {
|
||||
bool sync_keys = false, sync_values = false;
|
||||
unsigned size;
|
||||
int r;
|
||||
|
||||
assert(cache);
|
||||
assert(cache->hashmap);
|
||||
|
||||
size = n_entries(cache->hashmap);
|
||||
|
||||
if (res_keys) {
|
||||
r = cachemem_maintain(&cache->keys, size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sync_keys = r;
|
||||
} else
|
||||
cache->keys.active = false;
|
||||
|
||||
if (res_values) {
|
||||
r = cachemem_maintain(&cache->values, size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sync_values = r;
|
||||
} else
|
||||
cache->values.active = false;
|
||||
|
||||
if (cache->hashmap->dirty) {
|
||||
if (cache->keys.active)
|
||||
sync_keys = true;
|
||||
if (cache->values.active)
|
||||
sync_values = true;
|
||||
|
||||
cache->hashmap->dirty = false;
|
||||
}
|
||||
|
||||
if (sync_keys || sync_values) {
|
||||
unsigned i, idx;
|
||||
Iterator iter;
|
||||
|
||||
i = 0;
|
||||
HASHMAP_FOREACH_IDX(idx, cache->hashmap, iter) {
|
||||
struct hashmap_base_entry *e;
|
||||
|
||||
e = bucket_at(cache->hashmap, idx);
|
||||
|
||||
if (sync_keys)
|
||||
cache->keys.ptr[i] = e->key;
|
||||
if (sync_values)
|
||||
cache->values.ptr[i] = entry_value(cache->hashmap, e);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (res_keys)
|
||||
*res_keys = cache->keys.ptr;
|
||||
if (res_values)
|
||||
*res_values = cache->values.ptr;
|
||||
if (res_n_entries)
|
||||
*res_n_entries = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IteratedCache *iterated_cache_free(IteratedCache *cache) {
|
||||
if (cache) {
|
||||
free(cache->keys.ptr);
|
||||
free(cache->values.ptr);
|
||||
free(cache);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ typedef struct Hashmap Hashmap; /* Maps keys to values */
|
|||
typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
|
||||
typedef struct Set Set; /* Stores just keys */
|
||||
|
||||
typedef struct IteratedCache IteratedCache; /* Caches the iterated order of one of the above */
|
||||
|
||||
/* Ideally the Iterator would be an opaque struct, but it is instantiated
|
||||
* by hashmap users, so the definition has to be here. Do not use its fields
|
||||
* directly. */
|
||||
|
|
@ -126,6 +128,9 @@ static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h)
|
|||
return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
|
||||
}
|
||||
|
||||
IteratedCache *iterated_cache_free(IteratedCache *cache);
|
||||
int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries);
|
||||
|
||||
HashmapBase *internal_hashmap_copy(HashmapBase *h);
|
||||
static inline Hashmap *hashmap_copy(Hashmap *h) {
|
||||
return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
|
||||
|
|
@ -139,6 +144,14 @@ int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct h
|
|||
#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
|
||||
#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
|
||||
|
||||
IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h);
|
||||
static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) {
|
||||
return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
|
||||
}
|
||||
static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) {
|
||||
return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
|
||||
}
|
||||
|
||||
int hashmap_put(Hashmap *h, const void *key, void *value);
|
||||
static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
|
||||
return hashmap_put(PLAIN_HASHMAP(h), key, value);
|
||||
|
|
@ -394,3 +407,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
|
|||
#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
|
||||
#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
|
||||
#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free);
|
||||
|
||||
#define _cleanup_iterated_cache_free_ _cleanup_(iterated_cache_freep)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@ 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) {
|
||||
const char *suffix;
|
||||
int r, ifi = 0;
|
||||
|
|
@ -373,6 +374,7 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u
|
|||
|
||||
return r;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
|
||||
assert(addr);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ int flush_fd(int fd) {
|
|||
.fd = fd,
|
||||
.events = POLLIN,
|
||||
};
|
||||
int count = 0;
|
||||
|
||||
/* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
|
||||
* read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read
|
||||
|
|
@ -54,7 +55,7 @@ int flush_fd(int fd) {
|
|||
return -errno;
|
||||
|
||||
} else if (r == 0)
|
||||
return 0;
|
||||
return count;
|
||||
|
||||
l = read(fd, buf, sizeof(buf));
|
||||
if (l < 0) {
|
||||
|
|
@ -63,11 +64,13 @@ int flush_fd(int fd) {
|
|||
continue;
|
||||
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
return count;
|
||||
|
||||
return -errno;
|
||||
} else if (l == 0)
|
||||
return 0;
|
||||
return count;
|
||||
|
||||
count += (int) l;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,18 +20,16 @@
|
|||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "macro.h"
|
||||
#include "process-util.h"
|
||||
|
||||
/* Some structures we reference but don't want to pull in headers for */
|
||||
struct iovec;
|
||||
struct signalfd_siginfo;
|
||||
|
||||
typedef enum LogRealm {
|
||||
LOG_REALM_SYSTEMD,
|
||||
|
|
@ -52,7 +50,6 @@ typedef enum LogTarget{
|
|||
LOG_TARGET_SYSLOG,
|
||||
LOG_TARGET_SYSLOG_OR_KMSG,
|
||||
LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
|
||||
LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
|
||||
LOG_TARGET_NULL,
|
||||
_LOG_TARGET_MAX,
|
||||
_LOG_TARGET_INVALID = -1
|
||||
|
|
@ -99,11 +96,6 @@ int log_open(void);
|
|||
void log_close(void);
|
||||
void log_forget_fds(void);
|
||||
|
||||
void log_close_syslog(void);
|
||||
void log_close_journal(void);
|
||||
void log_close_kmsg(void);
|
||||
void log_close_console(void);
|
||||
|
||||
void log_parse_environment_realm(LogRealm realm);
|
||||
#define log_parse_environment() \
|
||||
log_parse_environment_realm(LOG_REALM)
|
||||
|
|
@ -199,7 +191,7 @@ int log_struct_iovec_internal(
|
|||
const char *file,
|
||||
int line,
|
||||
const char *func,
|
||||
const struct iovec input_iovec[],
|
||||
const struct iovec *input_iovec,
|
||||
size_t n_input_iovec);
|
||||
|
||||
/* This modifies the buffer passed! */
|
||||
|
|
@ -246,9 +238,9 @@ void log_assert_failed_return_realm(
|
|||
/* Logging with level */
|
||||
#define log_full_errno_realm(realm, level, error, ...) \
|
||||
({ \
|
||||
int _level = (level), _e = (error); \
|
||||
(log_get_max_level_realm((realm)) >= LOG_PRI(_level)) \
|
||||
? log_internal_realm(LOG_REALM_PLUS_LEVEL((realm), _level), _e, \
|
||||
int _level = (level), _e = (error), _realm = (realm); \
|
||||
(log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \
|
||||
? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \
|
||||
__FILE__, __LINE__, __func__, __VA_ARGS__) \
|
||||
: -abs(_e); \
|
||||
})
|
||||
|
|
@ -258,13 +250,15 @@ void log_assert_failed_return_realm(
|
|||
|
||||
#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__)
|
||||
|
||||
int log_emergency_level(void);
|
||||
|
||||
/* Normal logging */
|
||||
#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
|
||||
#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
|
||||
#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
|
||||
#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
|
||||
#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
|
||||
#define log_emergency(...) log_full(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
|
||||
#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__)
|
||||
|
||||
/* Logging triggered by an errno-like error */
|
||||
#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
|
||||
|
|
@ -272,7 +266,7 @@ void log_assert_failed_return_realm(
|
|||
#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
|
||||
#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
|
||||
#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
|
||||
#define log_emergency_errno(error, ...) log_full_errno(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
|
||||
#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__)
|
||||
|
||||
#ifdef LOG_TRACE
|
||||
# define log_trace(...) log_debug(__VA_ARGS__)
|
||||
|
|
@ -308,10 +302,20 @@ LogTarget log_target_from_string(const char *s) _pure_;
|
|||
|
||||
void log_received_signal(int level, const struct signalfd_siginfo *si);
|
||||
|
||||
/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */
|
||||
void log_set_upgrade_syslog_to_journal(bool b);
|
||||
|
||||
/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */
|
||||
void log_set_always_reopen_console(bool b);
|
||||
|
||||
/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not
|
||||
* desired as we want to reuse our logging streams. It is useful however */
|
||||
void log_set_open_when_needed(bool b);
|
||||
|
||||
/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to
|
||||
* stderr, the console or kmsg */
|
||||
void log_set_prohibit_ipc(bool b);
|
||||
|
||||
int log_syntax_internal(
|
||||
const char *unit,
|
||||
int level,
|
||||
|
|
@ -323,6 +327,16 @@ int log_syntax_internal(
|
|||
const char *func,
|
||||
const char *format, ...) _printf_(9, 10);
|
||||
|
||||
int log_syntax_invalid_utf8_internal(
|
||||
const char *unit,
|
||||
int level,
|
||||
const char *config_file,
|
||||
unsigned config_line,
|
||||
const char *file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *rvalue);
|
||||
|
||||
#define log_syntax(unit, level, config_file, config_line, error, ...) \
|
||||
({ \
|
||||
int _level = (level), _e = (error); \
|
||||
|
|
@ -334,12 +348,9 @@ int log_syntax_internal(
|
|||
#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
|
||||
({ \
|
||||
int _level = (level); \
|
||||
if (log_get_max_level() >= LOG_PRI(_level)) { \
|
||||
_cleanup_free_ char *_p = NULL; \
|
||||
_p = utf8_escape_invalid(rvalue); \
|
||||
log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
|
||||
"String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
|
||||
} \
|
||||
(log_get_max_level() >= LOG_PRI(_level)) \
|
||||
? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \
|
||||
: -EINVAL; \
|
||||
})
|
||||
|
||||
#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
|
||||
|
|
|
|||
|
|
@ -148,11 +148,17 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
|
|||
return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
|
||||
}
|
||||
|
||||
#ifndef __COVERITY__
|
||||
# define VOID_0 ((void)0)
|
||||
#else
|
||||
# define VOID_0 ((void*)0)
|
||||
#endif
|
||||
|
||||
#define ELEMENTSOF(x) \
|
||||
__extension__ (__builtin_choose_expr( \
|
||||
!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
|
||||
sizeof(x)/sizeof((x)[0]), \
|
||||
(void)0))
|
||||
VOID_0))
|
||||
|
||||
/*
|
||||
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
|
||||
|
|
@ -190,7 +196,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
|
|||
__builtin_constant_p(_B) && \
|
||||
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
|
||||
((_A) > (_B)) ? (_A) : (_B), \
|
||||
(void)0))
|
||||
VOID_0))
|
||||
|
||||
/* takes two types and returns the size of the larger one */
|
||||
#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "alloc-util.h"
|
||||
#include "errno-list.h"
|
||||
#include "extract-word.h"
|
||||
#include "locale-util.h"
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
#include "process-util.h"
|
||||
|
|
@ -70,7 +71,6 @@ int parse_pid(const char *s, pid_t* ret_pid) {
|
|||
*ret_pid = pid;
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int parse_mode(const char *s, mode_t *ret) {
|
||||
char *x;
|
||||
|
|
@ -87,7 +87,7 @@ int parse_mode(const char *s, mode_t *ret) {
|
|||
l = strtol(s, &x, 8);
|
||||
if (errno > 0)
|
||||
return -errno;
|
||||
if (!x || x == s || *x)
|
||||
if (!x || x == s || *x != 0)
|
||||
return -EINVAL;
|
||||
if (l < 0 || l > 07777)
|
||||
return -ERANGE;
|
||||
|
|
@ -237,7 +237,6 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int parse_range(const char *t, unsigned *lower, unsigned *upper) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
unsigned l, u;
|
||||
|
|
@ -288,7 +287,8 @@ int parse_errno(const char *t) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (e < 0 || e > ERRNO_MAX)
|
||||
/* 0 is also allowed here */
|
||||
if (!errno_is_valid(e) && e != 0)
|
||||
return -ERANGE;
|
||||
|
||||
return e;
|
||||
|
|
@ -332,7 +332,6 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
char *format_bytes(char *buf, size_t l, uint64_t t) {
|
||||
unsigned i;
|
||||
|
|
@ -374,6 +373,7 @@ finish:
|
|||
return buf;
|
||||
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int safe_atou(const char *s, unsigned *ret_u) {
|
||||
char *x = NULL;
|
||||
|
|
@ -396,7 +396,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
|
|||
l = strtoul(s, &x, 0);
|
||||
if (errno > 0)
|
||||
return -errno;
|
||||
if (!x || x == s || *x)
|
||||
if (!x || x == s || *x != 0)
|
||||
return -EINVAL;
|
||||
if (s[0] == '-')
|
||||
return -ERANGE;
|
||||
|
|
@ -418,7 +418,7 @@ int safe_atoi(const char *s, int *ret_i) {
|
|||
l = strtol(s, &x, 0);
|
||||
if (errno > 0)
|
||||
return -errno;
|
||||
if (!x || x == s || *x)
|
||||
if (!x || x == s || *x != 0)
|
||||
return -EINVAL;
|
||||
if ((long) (int) l != l)
|
||||
return -ERANGE;
|
||||
|
|
@ -440,7 +440,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
|
|||
l = strtoull(s, &x, 0);
|
||||
if (errno > 0)
|
||||
return -errno;
|
||||
if (!x || x == s || *x)
|
||||
if (!x || x == s || *x != 0)
|
||||
return -EINVAL;
|
||||
if (*s == '-')
|
||||
return -ERANGE;
|
||||
|
|
@ -460,7 +460,7 @@ int safe_atolli(const char *s, long long int *ret_lli) {
|
|||
l = strtoll(s, &x, 0);
|
||||
if (errno > 0)
|
||||
return -errno;
|
||||
if (!x || x == s || *x)
|
||||
if (!x || x == s || *x != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*ret_lli = l;
|
||||
|
|
@ -480,7 +480,7 @@ int safe_atou8(const char *s, uint8_t *ret) {
|
|||
l = strtoul(s, &x, 0);
|
||||
if (errno > 0)
|
||||
return -errno;
|
||||
if (!x || x == s || *x)
|
||||
if (!x || x == s || *x != 0)
|
||||
return -EINVAL;
|
||||
if (s[0] == '-')
|
||||
return -ERANGE;
|
||||
|
|
@ -504,7 +504,7 @@ int safe_atou16(const char *s, uint16_t *ret) {
|
|||
l = strtoul(s, &x, 0);
|
||||
if (errno > 0)
|
||||
return -errno;
|
||||
if (!x || x == s || *x)
|
||||
if (!x || x == s || *x != 0)
|
||||
return -EINVAL;
|
||||
if (s[0] == '-')
|
||||
return -ERANGE;
|
||||
|
|
@ -526,7 +526,7 @@ int safe_atoi16(const char *s, int16_t *ret) {
|
|||
l = strtol(s, &x, 0);
|
||||
if (errno > 0)
|
||||
return -errno;
|
||||
if (!x || x == s || *x)
|
||||
if (!x || x == s || *x != 0)
|
||||
return -EINVAL;
|
||||
if ((long) (int16_t) l != l)
|
||||
return -ERANGE;
|
||||
|
|
@ -535,10 +535,11 @@ int safe_atoi16(const char *s, int16_t *ret) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int safe_atod(const char *s, double *ret_d) {
|
||||
_cleanup_(freelocalep) locale_t loc = (locale_t) 0;
|
||||
char *x = NULL;
|
||||
double d = 0;
|
||||
locale_t loc;
|
||||
|
||||
assert(s);
|
||||
assert(ret_d);
|
||||
|
|
@ -549,16 +550,11 @@ int safe_atod(const char *s, double *ret_d) {
|
|||
|
||||
errno = 0;
|
||||
d = strtod_l(s, &x, loc);
|
||||
if (errno > 0) {
|
||||
freelocale(loc);
|
||||
if (errno > 0)
|
||||
return -errno;
|
||||
}
|
||||
if (!x || x == s || *x) {
|
||||
freelocale(loc);
|
||||
if (!x || x == s || *x != 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
freelocale(loc);
|
||||
*ret_d = (double) d;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -601,19 +597,20 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
|
|||
|
||||
int parse_percent_unbounded(const char *p) {
|
||||
const char *pc, *n;
|
||||
unsigned v;
|
||||
int r;
|
||||
int r, v;
|
||||
|
||||
pc = endswith(p, "%");
|
||||
if (!pc)
|
||||
return -EINVAL;
|
||||
|
||||
n = strndupa(p, pc - p);
|
||||
r = safe_atou(n, &v);
|
||||
r = safe_atoi(n, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (v < 0)
|
||||
return -ERANGE;
|
||||
|
||||
return (int) v;
|
||||
return v;
|
||||
}
|
||||
|
||||
int parse_percent(const char *p) {
|
||||
|
|
@ -626,7 +623,6 @@ int parse_percent(const char *p) {
|
|||
return v;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int parse_nice(const char *p, int *ret) {
|
||||
int n, r;
|
||||
|
||||
|
|
|
|||
|
|
@ -84,14 +84,36 @@ char *path_make_absolute(const char *p, const char *prefix) {
|
|||
/* Makes every item in the list an absolute path by prepending
|
||||
* the prefix, if specified and necessary */
|
||||
|
||||
if (path_is_absolute(p) || !prefix)
|
||||
if (path_is_absolute(p) || isempty(prefix))
|
||||
return strdup(p);
|
||||
|
||||
return strjoin(prefix, "/", p);
|
||||
if (endswith(prefix, "/"))
|
||||
return strjoin(prefix, p);
|
||||
else
|
||||
return strjoin(prefix, "/", p);
|
||||
}
|
||||
|
||||
int safe_getcwd(char **ret) {
|
||||
char *cwd;
|
||||
|
||||
cwd = get_current_dir_name();
|
||||
if (!cwd)
|
||||
return negative_errno();
|
||||
|
||||
/* Let's make sure the directory is really absolute, to protect us from the logic behind
|
||||
* CVE-2018-1000001 */
|
||||
if (cwd[0] != '/') {
|
||||
free(cwd);
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
*ret = cwd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int path_make_absolute_cwd(const char *p, char **ret) {
|
||||
char *c;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
|
@ -104,11 +126,14 @@ int path_make_absolute_cwd(const char *p, char **ret) {
|
|||
else {
|
||||
_cleanup_free_ char *cwd = NULL;
|
||||
|
||||
cwd = get_current_dir_name();
|
||||
if (!cwd)
|
||||
return negative_errno();
|
||||
r = safe_getcwd(&cwd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c = strjoin(cwd, "/", p);
|
||||
if (endswith(cwd, "/"))
|
||||
c = strjoin(cwd, p);
|
||||
else
|
||||
c = strjoin(cwd, "/", p);
|
||||
}
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ bool is_path(const char *p) _pure_;
|
|||
int path_split_and_make_absolute(const char *p, char ***ret);
|
||||
bool path_is_absolute(const char *p) _pure_;
|
||||
char* path_make_absolute(const char *p, const char *prefix);
|
||||
int safe_getcwd(char **ret);
|
||||
int path_make_absolute_cwd(const char *p, char **ret);
|
||||
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
|
||||
char* path_kill_slashes(char *path);
|
||||
|
|
|
|||
|
|
@ -692,32 +692,43 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
|
|||
* A warning is emitted if the process terminates abnormally,
|
||||
* and also if it returns non-zero unless check_exit_code is true.
|
||||
*/
|
||||
int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
|
||||
int r;
|
||||
int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
|
||||
_cleanup_free_ char *buffer = NULL;
|
||||
siginfo_t status;
|
||||
int r, prio;
|
||||
|
||||
assert(name);
|
||||
assert(pid > 1);
|
||||
|
||||
if (!name) {
|
||||
r = get_process_comm(pid, &buffer);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
|
||||
else
|
||||
name = buffer;
|
||||
}
|
||||
|
||||
prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
|
||||
|
||||
r = wait_for_terminate(pid, &status);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to wait for %s: %m", name);
|
||||
return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
|
||||
|
||||
if (status.si_code == CLD_EXITED) {
|
||||
if (status.si_status != 0)
|
||||
log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
|
||||
"%s failed with error code %i.", name, status.si_status);
|
||||
if (status.si_status != EXIT_SUCCESS)
|
||||
log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG,
|
||||
"%s failed with exit status %i.", strna(name), status.si_status);
|
||||
else
|
||||
log_debug("%s succeeded.", name);
|
||||
|
||||
return status.si_status;
|
||||
|
||||
} else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) {
|
||||
|
||||
log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
|
||||
log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status));
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
log_warning("%s failed due to unknown reason.", name);
|
||||
log_full(prio, "%s failed due to unknown reason.", strna(name));
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -790,6 +801,8 @@ void sigkill_wait(pid_t pid) {
|
|||
}
|
||||
|
||||
void sigkill_waitp(pid_t *pid) {
|
||||
PROTECT_ERRNO;
|
||||
|
||||
if (!pid)
|
||||
return;
|
||||
if (*pid <= 1)
|
||||
|
|
@ -798,6 +811,13 @@ void sigkill_waitp(pid_t *pid) {
|
|||
sigkill_wait(*pid);
|
||||
}
|
||||
|
||||
void sigterm_wait(pid_t pid) {
|
||||
assert(pid > 1);
|
||||
|
||||
if (kill_and_sigcont(pid, SIGTERM) > 0)
|
||||
(void) wait_for_terminate(pid, NULL);
|
||||
}
|
||||
|
||||
int kill_and_sigcont(pid_t pid, int sig) {
|
||||
int r;
|
||||
|
||||
|
|
@ -943,6 +963,17 @@ noreturn void freeze(void) {
|
|||
|
||||
sync();
|
||||
|
||||
/* Let's not freeze right away, but keep reaping zombies. */
|
||||
for (;;) {
|
||||
int r;
|
||||
siginfo_t si = {};
|
||||
|
||||
r = waitid(P_ALL, 0, &si, WEXITED);
|
||||
if (r < 0 && errno != EINTR)
|
||||
break;
|
||||
}
|
||||
|
||||
/* waitid() failed with an unexpected error, things are really borked. Freeze now! */
|
||||
for (;;)
|
||||
pause();
|
||||
}
|
||||
|
|
@ -1091,7 +1122,7 @@ int ioprio_parse_priority(const char *s, int *ret) {
|
|||
|
||||
static pid_t cached_pid = CACHED_PID_UNSET;
|
||||
|
||||
static void reset_cached_pid(void) {
|
||||
void reset_cached_pid(void) {
|
||||
/* Invoked in the child after a fork(), i.e. at the first moment the PID changed */
|
||||
cached_pid = CACHED_PID_UNSET;
|
||||
}
|
||||
|
|
@ -1121,7 +1152,7 @@ pid_t getpid_cached(void) {
|
|||
case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */
|
||||
pid_t new_pid;
|
||||
|
||||
new_pid = getpid();
|
||||
new_pid = raw_getpid();
|
||||
|
||||
if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) {
|
||||
/* OOM? Let's try again later */
|
||||
|
|
@ -1134,7 +1165,7 @@ pid_t getpid_cached(void) {
|
|||
}
|
||||
|
||||
case CACHED_PID_BUSY: /* Somebody else is currently initializing */
|
||||
return getpid();
|
||||
return raw_getpid();
|
||||
|
||||
default: /* Properly initialized */
|
||||
return current_value;
|
||||
|
|
@ -1159,46 +1190,72 @@ int safe_fork_full(
|
|||
pid_t *ret_pid) {
|
||||
|
||||
pid_t original_pid, pid;
|
||||
sigset_t saved_ss;
|
||||
bool block_signals;
|
||||
int r;
|
||||
sigset_t saved_ss, ss;
|
||||
bool block_signals = false;
|
||||
int prio, r;
|
||||
|
||||
/* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
|
||||
* returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
|
||||
|
||||
prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
|
||||
|
||||
original_pid = getpid_cached();
|
||||
|
||||
block_signals = flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG);
|
||||
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
|
||||
|
||||
if (block_signals) {
|
||||
sigset_t ss;
|
||||
/* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
|
||||
* be sure that SIGTERMs are not lost we might send to the child. */
|
||||
|
||||
/* We temporarily block all signals, so that the new child has them blocked initially. This way, we can be sure
|
||||
* that SIGTERMs are not lost we might send to the child. */
|
||||
if (sigfillset(&ss) < 0)
|
||||
return log_debug_errno(errno, "Failed to reset signal set: %m");
|
||||
return log_full_errno(prio, errno, "Failed to reset signal set: %m");
|
||||
|
||||
if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
|
||||
return log_debug_errno(errno, "Failed to reset signal mask: %m");
|
||||
block_signals = true;
|
||||
|
||||
} else if (flags & FORK_WAIT) {
|
||||
|
||||
/* Let's block SIGCHLD at least, so that we can safely watch for the child process */
|
||||
|
||||
if (sigemptyset(&ss) < 0)
|
||||
return log_full_errno(prio, errno, "Failed to clear signal set: %m");
|
||||
|
||||
if (sigaddset(&ss, SIGCHLD) < 0)
|
||||
return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m");
|
||||
|
||||
block_signals = true;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (block_signals)
|
||||
if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
|
||||
return log_full_errno(prio, errno, "Failed to set signal mask: %m");
|
||||
|
||||
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_debug_errno(r, "Failed to fork: %m");
|
||||
return log_full_errno(prio, r, "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) {
|
||||
r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
if (block_signals) /* undo what we did above */
|
||||
(void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
|
||||
|
||||
log_debug("Sucessfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
|
||||
|
||||
if (ret_pid)
|
||||
*ret_pid = pid;
|
||||
|
||||
|
|
@ -1216,40 +1273,45 @@ int safe_fork_full(
|
|||
if (name) {
|
||||
r = rename_process(name);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to rename process, ignoring: %m");
|
||||
log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG,
|
||||
r, "Failed to rename process, ignoring: %m");
|
||||
}
|
||||
|
||||
if (flags & FORK_DEATHSIG)
|
||||
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) {
|
||||
log_debug_errno(errno, "Failed to set death signal: %m");
|
||||
log_full_errno(prio, errno, "Failed to set death signal: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (flags & FORK_RESET_SIGNALS) {
|
||||
r = reset_all_signal_handlers();
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to reset signal handlers: %m");
|
||||
log_full_errno(prio, r, "Failed to reset signal handlers: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* This implicitly undoes the signal mask stuff we did before the fork()ing above */
|
||||
r = reset_signal_mask();
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to reset signal mask: %m");
|
||||
log_full_errno(prio, r, "Failed to reset signal mask: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (block_signals) { /* undo what we did above */
|
||||
if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) {
|
||||
log_debug_errno(errno, "Failed to restore signal mask: %m");
|
||||
log_full_errno(prio, errno, "Failed to restore signal mask: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & FORK_DEATHSIG) {
|
||||
pid_t ppid;
|
||||
/* Let's see if the parent PID is still the one we started from? If not, then the parent
|
||||
* already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
|
||||
|
||||
if (getppid() != original_pid) {
|
||||
ppid = getppid();
|
||||
if (ppid == 0)
|
||||
/* Parent is in a differn't PID namespace. */;
|
||||
else if (ppid != original_pid) {
|
||||
log_debug("Parent died early, raising SIGTERM.");
|
||||
(void) raise(SIGTERM);
|
||||
_exit(EXIT_FAILURE);
|
||||
|
|
@ -1262,7 +1324,7 @@ int safe_fork_full(
|
|||
|
||||
r = close_all_fds(except_fds, n_except_fds);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to close all file descriptors: %m");
|
||||
log_full_errno(prio, r, "Failed to close all file descriptors: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
|
@ -1276,7 +1338,7 @@ int safe_fork_full(
|
|||
if (flags & FORK_NULL_STDIO) {
|
||||
r = make_null_stdio();
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to connect stdin/stdout to /dev/null: %m");
|
||||
log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
***/
|
||||
|
||||
#include <alloca.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
|
@ -61,11 +62,21 @@ int get_process_environ(pid_t pid, char **environ);
|
|||
int get_process_ppid(pid_t pid, pid_t *ppid);
|
||||
|
||||
int wait_for_terminate(pid_t pid, siginfo_t *status);
|
||||
int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
|
||||
|
||||
typedef enum WaitFlags {
|
||||
WAIT_LOG_ABNORMAL = 1U << 0,
|
||||
WAIT_LOG_NON_ZERO_EXIT_STATUS = 1U << 1,
|
||||
|
||||
/* A shortcut for requesting the most complete logging */
|
||||
WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
|
||||
} WaitFlags;
|
||||
|
||||
int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags);
|
||||
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
|
||||
|
||||
void sigkill_wait(pid_t pid);
|
||||
void sigkill_waitp(pid_t *pid);
|
||||
void sigterm_wait(pid_t pid);
|
||||
|
||||
int kill_and_sigcont(pid_t pid, int sig);
|
||||
|
||||
|
|
@ -106,8 +117,13 @@ int sigchld_code_from_string(const char *s) _pure_;
|
|||
int sched_policy_to_string_alloc(int i, char **s);
|
||||
int sched_policy_from_string(const char *s);
|
||||
|
||||
#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p))
|
||||
#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
|
||||
static inline pid_t PTR_TO_PID(const void *p) {
|
||||
return (pid_t) ((uintptr_t) p);
|
||||
}
|
||||
|
||||
static inline void* PID_TO_PTR(pid_t pid) {
|
||||
return (void*) ((uintptr_t) pid);
|
||||
}
|
||||
|
||||
void valgrind_summary_hack(void);
|
||||
|
||||
|
|
@ -137,11 +153,19 @@ static inline bool ioprio_priority_is_valid(int i) {
|
|||
static inline bool pid_is_valid(pid_t p) {
|
||||
return p > 0;
|
||||
}
|
||||
|
||||
static inline int sched_policy_to_string_alloc_with_check(int n, char **s) {
|
||||
if (!sched_policy_is_valid(n))
|
||||
return -EINVAL;
|
||||
|
||||
return sched_policy_to_string_alloc(n, s);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int ioprio_parse_priority(const char *s, int *ret);
|
||||
|
||||
pid_t getpid_cached(void);
|
||||
void reset_cached_pid(void);
|
||||
|
||||
int must_be_root(void);
|
||||
|
||||
|
|
@ -151,6 +175,9 @@ typedef enum ForkFlags {
|
|||
FORK_DEATHSIG = 1U << 2,
|
||||
FORK_NULL_STDIO = 1U << 3,
|
||||
FORK_REOPEN_LOG = 1U << 4,
|
||||
FORK_LOG = 1U << 5,
|
||||
FORK_WAIT = 1U << 6,
|
||||
FORK_NEW_MOUNTNS = 1U << 7,
|
||||
} ForkFlags;
|
||||
|
||||
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
|
||||
|
|
@ -160,3 +187,22 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
|
|||
}
|
||||
|
||||
int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *pid, const char *path, ...);
|
||||
|
||||
#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
|
||||
* the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the
|
||||
* upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by
|
||||
* 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since
|
||||
* 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)
|
||||
|
|
|
|||
|
|
@ -23,11 +23,12 @@
|
|||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <linux/random.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#if HAVE_SYS_AUXV_H
|
||||
# include <sys/auxv.h>
|
||||
|
|
|
|||
|
|
@ -55,3 +55,10 @@ static inline void block_signals_reset(sigset_t *ss) {
|
|||
static inline bool SIGNAL_VALID(int signo) {
|
||||
return signo > 0 && signo < _NSIG;
|
||||
}
|
||||
|
||||
static inline const char* signal_to_string_with_check(int n) {
|
||||
if (!SIGNAL_VALID(n))
|
||||
return NULL;
|
||||
|
||||
return signal_to_string(n);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#include "missing.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
|
|
@ -53,7 +54,7 @@
|
|||
|
||||
#if 0 /* NM_IGNORED */
|
||||
#if ENABLE_IDN
|
||||
# define IDN_FLAGS (NI_IDN|NI_IDN_USE_STD3_ASCII_RULES)
|
||||
# define IDN_FLAGS NI_IDN
|
||||
#else
|
||||
# define IDN_FLAGS 0
|
||||
#endif
|
||||
|
|
@ -71,7 +72,6 @@ DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int);
|
|||
|
||||
int socket_address_parse(SocketAddress *a, const char *s) {
|
||||
char *e, *n;
|
||||
unsigned u;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
|
|
@ -81,6 +81,8 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
|||
a->type = SOCK_STREAM;
|
||||
|
||||
if (*s == '[') {
|
||||
uint16_t port;
|
||||
|
||||
/* IPv6 in [x:.....:z]:p notation */
|
||||
|
||||
e = strchr(s+1, ']');
|
||||
|
|
@ -98,15 +100,12 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
|||
return -EINVAL;
|
||||
|
||||
e++;
|
||||
r = safe_atou(e, &u);
|
||||
r = parse_ip_port(e, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u <= 0 || u > 0xFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
|
||||
a->sockaddr.in6.sin6_port = htobe16(port);
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
|
||||
} else if (*s == '/') {
|
||||
|
|
@ -137,12 +136,13 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
|||
} 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, &u);
|
||||
r = safe_atou(e+1, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
@ -155,19 +155,18 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
|||
a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
|
||||
|
||||
a->sockaddr.vm.svm_family = AF_VSOCK;
|
||||
a->sockaddr.vm.svm_port = u;
|
||||
a->sockaddr.vm.svm_port = port;
|
||||
a->size = sizeof(struct sockaddr_vm);
|
||||
|
||||
} else {
|
||||
uint16_t port;
|
||||
|
||||
e = strchr(s, ':');
|
||||
if (e) {
|
||||
r = safe_atou(e+1, &u);
|
||||
r = parse_ip_port(e + 1, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u <= 0 || u > 0xFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
n = strndupa(s, e-s);
|
||||
|
||||
/* IPv4 in w.x.y.z:p notation? */
|
||||
|
|
@ -178,7 +177,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
|||
if (r > 0) {
|
||||
/* Gotcha, it's a traditional IPv4 address */
|
||||
a->sockaddr.in.sin_family = AF_INET;
|
||||
a->sockaddr.in.sin_port = htobe16((uint16_t)u);
|
||||
a->sockaddr.in.sin_port = htobe16(port);
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
} else {
|
||||
unsigned idx;
|
||||
|
|
@ -192,7 +191,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
|||
return -EINVAL;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
|
||||
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);
|
||||
|
|
@ -200,21 +199,18 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
|||
} else {
|
||||
|
||||
/* Just a port */
|
||||
r = safe_atou(s, &u);
|
||||
r = parse_ip_port(s, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u <= 0 || u > 0xFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
if (socket_ipv6_is_supported()) {
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
|
||||
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((uint16_t)u);
|
||||
a->sockaddr.in.sin_port = htobe16(port);
|
||||
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
}
|
||||
|
|
@ -765,19 +761,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int getnameinfo_pretty(int fd, char **ret) {
|
||||
union sockaddr_union sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
if (getsockname(fd, &sa.sa, &salen) < 0)
|
||||
return -errno;
|
||||
|
||||
return socknameinfo_pretty(&sa, salen, ret);
|
||||
}
|
||||
|
||||
int socket_address_unlink(SocketAddress *a) {
|
||||
assert(a);
|
||||
|
||||
|
|
@ -824,6 +807,18 @@ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIN
|
|||
|
||||
DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
|
||||
|
||||
SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *n) {
|
||||
int r;
|
||||
|
||||
r = parse_boolean(n);
|
||||
if (r > 0)
|
||||
return SOCKET_ADDRESS_IPV6_ONLY;
|
||||
if (r == 0)
|
||||
return SOCKET_ADDRESS_BOTH;
|
||||
|
||||
return socket_address_bind_ipv6_only_from_string(n);
|
||||
}
|
||||
|
||||
bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
|
@ -958,58 +953,80 @@ int getpeercred(int fd, struct ucred *ucred) {
|
|||
if (n != sizeof(struct ucred))
|
||||
return -EIO;
|
||||
|
||||
/* Check if the data is actually useful and not suppressed due
|
||||
* to namespacing issues */
|
||||
if (u.pid <= 0)
|
||||
return -ENODATA;
|
||||
if (u.uid == UID_INVALID)
|
||||
return -ENODATA;
|
||||
if (u.gid == GID_INVALID)
|
||||
/* Check if the data is actually useful and not suppressed due to namespacing issues */
|
||||
if (!pid_is_valid(u.pid))
|
||||
return -ENODATA;
|
||||
|
||||
/* Note that we don't check UID/GID here, as namespace translation works differently there: instead of
|
||||
* receiving in "invalid" user/group we get the overflow UID/GID. */
|
||||
|
||||
*ucred = u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getpeersec(int fd, char **ret) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
socklen_t n = 64;
|
||||
char *s;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
s = new0(char, n);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
for (;;) {
|
||||
s = new0(char, n+1);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
|
||||
if (r < 0) {
|
||||
free(s);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
|
||||
break;
|
||||
|
||||
if (errno != ERANGE)
|
||||
return -errno;
|
||||
|
||||
s = new0(char, n);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
|
||||
if (r < 0) {
|
||||
free(s);
|
||||
return -errno;
|
||||
}
|
||||
s = mfree(s);
|
||||
}
|
||||
|
||||
if (isempty(s)) {
|
||||
free(s);
|
||||
if (isempty(s))
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
*ret = s;
|
||||
s = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getpeergroups(int fd, gid_t **ret) {
|
||||
socklen_t n = sizeof(gid_t) * 64;
|
||||
_cleanup_free_ gid_t *d = NULL;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
d = malloc(n);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0)
|
||||
break;
|
||||
|
||||
if (errno != ERANGE)
|
||||
return -errno;
|
||||
|
||||
d = mfree(d);
|
||||
}
|
||||
|
||||
assert_se(n % sizeof(gid_t) == 0);
|
||||
n /= sizeof(gid_t);
|
||||
|
||||
if ((socklen_t) (int) n != n)
|
||||
return -E2BIG;
|
||||
|
||||
*ret = d;
|
||||
d = NULL;
|
||||
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
int send_one_fd_sa(
|
||||
int transport_fd,
|
||||
int fd,
|
||||
|
|
|
|||
|
|
@ -36,18 +36,28 @@
|
|||
#include "util.h"
|
||||
|
||||
union sockaddr_union {
|
||||
/* The minimal, abstract version */
|
||||
struct sockaddr sa;
|
||||
|
||||
/* The libc provided version that allocates "enough room" for every protocol */
|
||||
struct sockaddr_storage storage;
|
||||
|
||||
/* Protoctol-specific implementations */
|
||||
struct sockaddr_in in;
|
||||
struct sockaddr_in6 in6;
|
||||
struct sockaddr_un un;
|
||||
struct sockaddr_nl nl;
|
||||
struct sockaddr_storage storage;
|
||||
struct sockaddr_ll ll;
|
||||
#if 0 /* NM_IGNORED */
|
||||
struct sockaddr_vm vm;
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
/* Ensure there is enough space to store Infiniband addresses */
|
||||
uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)];
|
||||
|
||||
/* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path
|
||||
* component is always followed by at least one NUL byte. */
|
||||
uint8_t un_buffer[sizeof(struct sockaddr_un) + 1];
|
||||
};
|
||||
|
||||
typedef struct SocketAddress {
|
||||
|
|
@ -118,10 +128,10 @@ int getpeername_pretty(int fd, bool include_port, char **ret);
|
|||
int getsockname_pretty(int fd, char **ret);
|
||||
|
||||
int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
|
||||
int getnameinfo_pretty(int fd, char **ret);
|
||||
|
||||
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
|
||||
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
|
||||
SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *s);
|
||||
|
||||
int netlink_family_to_string_alloc(int b, char **s);
|
||||
int netlink_family_from_string(const char *s) _pure_;
|
||||
|
|
@ -139,6 +149,7 @@ bool address_label_valid(const char *p);
|
|||
|
||||
int getpeercred(int fd, struct ucred *ucred);
|
||||
int getpeersec(int fd, char **ret);
|
||||
int getpeergroups(int fd, gid_t **ret);
|
||||
|
||||
int send_one_fd_sa(int transport_fd,
|
||||
int fd,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "gunicode.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "terminal-util.h"
|
||||
#include "utf8.h"
|
||||
#include "util.h"
|
||||
|
||||
|
|
@ -654,7 +655,17 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
|
|||
return ret;
|
||||
}
|
||||
|
||||
char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
|
||||
if (!offsets)
|
||||
return;
|
||||
|
||||
if ((size_t) diff < offsets[0])
|
||||
shift[0] += size;
|
||||
if ((size_t) diff < offsets[1])
|
||||
shift[1] += size;
|
||||
}
|
||||
|
||||
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
|
||||
const char *i, *begin = NULL;
|
||||
enum {
|
||||
STATE_OTHER,
|
||||
|
|
@ -662,7 +673,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
|||
STATE_BRACKET
|
||||
} state = STATE_OTHER;
|
||||
char *obuf = NULL;
|
||||
size_t osz = 0, isz;
|
||||
size_t osz = 0, isz, shift[2] = {};
|
||||
FILE *f;
|
||||
|
||||
assert(ibuf);
|
||||
|
|
@ -690,15 +701,18 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
|||
break;
|
||||
else if (*i == '\x1B')
|
||||
state = STATE_ESCAPE;
|
||||
else if (*i == '\t')
|
||||
else if (*i == '\t') {
|
||||
fputs(" ", f);
|
||||
else
|
||||
advance_offsets(i - *ibuf, highlight, shift, 7);
|
||||
} else
|
||||
fputc(*i, f);
|
||||
|
||||
break;
|
||||
|
||||
case STATE_ESCAPE:
|
||||
if (i >= *ibuf + isz) { /* EOT */
|
||||
fputc('\x1B', f);
|
||||
advance_offsets(i - *ibuf, highlight, shift, 1);
|
||||
break;
|
||||
} else if (*i == '[') {
|
||||
state = STATE_BRACKET;
|
||||
|
|
@ -706,6 +720,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
|||
} else {
|
||||
fputc('\x1B', f);
|
||||
fputc(*i, f);
|
||||
advance_offsets(i - *ibuf, highlight, shift, 1);
|
||||
state = STATE_OTHER;
|
||||
}
|
||||
|
||||
|
|
@ -717,6 +732,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
|||
(!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
|
||||
fputc('\x1B', f);
|
||||
fputc('[', f);
|
||||
advance_offsets(i - *ibuf, highlight, shift, 2);
|
||||
state = STATE_OTHER;
|
||||
i = begin-1;
|
||||
} else if (*i == 'm')
|
||||
|
|
@ -738,6 +754,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
|||
if (_isz)
|
||||
*_isz = osz;
|
||||
|
||||
if (highlight) {
|
||||
highlight[0] += shift[0];
|
||||
highlight[1] += shift[1];
|
||||
}
|
||||
|
||||
return obuf;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,15 +52,15 @@ static inline bool streq_ptr(const char *a, const char *b) {
|
|||
}
|
||||
|
||||
static inline const char* strempty(const char *s) {
|
||||
return s ? s : "";
|
||||
return s ?: "";
|
||||
}
|
||||
|
||||
static inline const char* strnull(const char *s) {
|
||||
return s ? s : "(null)";
|
||||
return s ?: "(null)";
|
||||
}
|
||||
|
||||
static inline const char *strna(const char *s) {
|
||||
return s ? s : "n/a";
|
||||
return s ?: "n/a";
|
||||
}
|
||||
|
||||
static inline bool isempty(const char *p) {
|
||||
|
|
@ -177,7 +177,7 @@ char* strshorten(char *s, size_t l);
|
|||
|
||||
char *strreplace(const char *text, const char *old_string, const char *new_string);
|
||||
|
||||
char *strip_tab_ansi(char **p, size_t *l);
|
||||
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
|
||||
|
||||
char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
|
||||
|
||||
|
|
|
|||
|
|
@ -397,42 +397,6 @@ char *strv_join(char **l, const char *separator) {
|
|||
return r;
|
||||
}
|
||||
|
||||
char *strv_join_quoted(char **l) {
|
||||
char *buf = NULL;
|
||||
char **s;
|
||||
size_t allocated = 0, len = 0;
|
||||
|
||||
STRV_FOREACH(s, l) {
|
||||
/* assuming here that escaped string cannot be more
|
||||
* than twice as long, and reserving space for the
|
||||
* separator and quotes.
|
||||
*/
|
||||
_cleanup_free_ char *esc = NULL;
|
||||
size_t needed;
|
||||
|
||||
if (!GREEDY_REALLOC(buf, allocated,
|
||||
len + strlen(*s) * 2 + 3))
|
||||
goto oom;
|
||||
|
||||
esc = cescape(*s);
|
||||
if (!esc)
|
||||
goto oom;
|
||||
|
||||
needed = snprintf(buf + len, allocated - len, "%s\"%s\"",
|
||||
len > 0 ? " " : "", esc);
|
||||
assert(needed < allocated - len);
|
||||
len += needed;
|
||||
}
|
||||
|
||||
if (!buf)
|
||||
buf = malloc0(1);
|
||||
|
||||
return buf;
|
||||
|
||||
oom:
|
||||
return mfree(buf);
|
||||
}
|
||||
|
||||
int strv_push(char ***l, char *value) {
|
||||
char **c;
|
||||
unsigned n, m;
|
||||
|
|
@ -486,7 +450,7 @@ int strv_push_pair(char ***l, char *a, char *b) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int strv_push_prepend(char ***l, char *value) {
|
||||
int strv_insert(char ***l, unsigned position, char *value) {
|
||||
char **c;
|
||||
unsigned n, m, i;
|
||||
|
||||
|
|
@ -494,6 +458,7 @@ int strv_push_prepend(char ***l, char *value) {
|
|||
return 0;
|
||||
|
||||
n = strv_length(*l);
|
||||
position = MIN(position, n);
|
||||
|
||||
/* increase and check for overflow */
|
||||
m = n + 2;
|
||||
|
|
@ -504,10 +469,12 @@ int strv_push_prepend(char ***l, char *value) {
|
|||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (i = 0; i < position; i++)
|
||||
c[i] = (*l)[i];
|
||||
c[position] = value;
|
||||
for (i = position; i < n; i++)
|
||||
c[i+1] = (*l)[i];
|
||||
|
||||
c[0] = value;
|
||||
c[n+1] = NULL;
|
||||
|
||||
free(*l);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,12 @@ int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
|
|||
int strv_extend_front(char ***l, const char *value);
|
||||
int strv_push(char ***l, char *value);
|
||||
int strv_push_pair(char ***l, char *a, char *b);
|
||||
int strv_push_prepend(char ***l, char *value);
|
||||
int strv_insert(char ***l, unsigned position, char *value);
|
||||
|
||||
static inline int strv_push_prepend(char ***l, char *value) {
|
||||
return strv_insert(l, 0, value);
|
||||
}
|
||||
|
||||
int strv_consume(char ***l, char *value);
|
||||
int strv_consume_pair(char ***l, char *a, char *b);
|
||||
int strv_consume_prepend(char ***l, char *value);
|
||||
|
|
@ -86,7 +91,6 @@ char **strv_split_newlines(const char *s);
|
|||
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
|
||||
|
||||
char *strv_join(char **l, const char *separator);
|
||||
char *strv_join_quoted(char **l);
|
||||
|
||||
char **strv_parse_nulstr(const char *s, size_t l);
|
||||
char **strv_split_nulstr(const char *s);
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
|
|
@ -891,7 +892,6 @@ typedef struct ParseTimestampResult {
|
|||
int parse_timestamp(const char *t, usec_t *usec) {
|
||||
char *last_space, *tz = NULL;
|
||||
ParseTimestampResult *shared, tmp;
|
||||
pid_t pid;
|
||||
int r;
|
||||
|
||||
last_space = strrchr(t, ' ');
|
||||
|
|
@ -905,7 +905,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
|||
if (shared == MAP_FAILED)
|
||||
return negative_errno();
|
||||
|
||||
r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG, &pid);
|
||||
r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
|
||||
if (r < 0) {
|
||||
(void) munmap(shared, sizeof *shared);
|
||||
return r;
|
||||
|
|
@ -933,12 +933,6 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
|||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
r = wait_for_terminate(pid, NULL);
|
||||
if (r < 0) {
|
||||
(void) munmap(shared, sizeof *shared);
|
||||
return r;
|
||||
}
|
||||
|
||||
tmp = *shared;
|
||||
if (munmap(shared, sizeof *shared) != 0)
|
||||
return negative_errno();
|
||||
|
|
|
|||
|
|
@ -100,12 +100,12 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
|
|||
#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \
|
||||
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM)
|
||||
|
||||
static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
|
||||
static inline bool dual_timestamp_is_set(const dual_timestamp *ts) {
|
||||
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
|
||||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
|
||||
}
|
||||
|
||||
static inline bool triple_timestamp_is_set(triple_timestamp *ts) {
|
||||
static inline bool triple_timestamp_is_set(const triple_timestamp *ts) {
|
||||
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
|
||||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) ||
|
||||
(ts->boottime > 0 && ts->boottime != USEC_INFINITY));
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "procfs-util.h"
|
||||
#include "set.h"
|
||||
#include "signal-util.h"
|
||||
#include "stat-util.h"
|
||||
|
|
@ -63,6 +64,7 @@
|
|||
#include "umask-util.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
#include "virt.h"
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int saved_argc = 0;
|
||||
|
|
@ -477,31 +479,22 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
|
|||
|
||||
uint64_t system_tasks_max(void) {
|
||||
|
||||
#if SIZEOF_PID_T == 4
|
||||
#define TASKS_MAX ((uint64_t) (INT32_MAX-1))
|
||||
#elif SIZEOF_PID_T == 2
|
||||
#define TASKS_MAX ((uint64_t) (INT16_MAX-1))
|
||||
#else
|
||||
#error "Unknown pid_t size"
|
||||
#endif
|
||||
|
||||
_cleanup_free_ char *value = NULL, *root = NULL;
|
||||
uint64_t a = TASKS_MAX, b = TASKS_MAX;
|
||||
_cleanup_free_ char *root = NULL;
|
||||
|
||||
/* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
|
||||
* limit:
|
||||
*
|
||||
* a) the maximum value for the pid_t type
|
||||
* a) the maximum tasks value the kernel allows on this architecture
|
||||
* b) the cgroups pids_max attribute for the system
|
||||
* c) the kernel's configure maximum PID value
|
||||
* c) the kernel's configured maximum PID value
|
||||
*
|
||||
* And then pick the smallest of the three */
|
||||
|
||||
if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0)
|
||||
(void) safe_atou64(value, &a);
|
||||
(void) procfs_tasks_get_limit(&a);
|
||||
|
||||
if (cg_get_root_path(&root) >= 0) {
|
||||
value = mfree(value);
|
||||
_cleanup_free_ char *value = NULL;
|
||||
|
||||
if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
|
||||
(void) safe_atou64(value, &b);
|
||||
|
|
@ -620,4 +613,16 @@ int str_verscmp(const char *s1, const char *s2) {
|
|||
|
||||
return strcmp(os1, os2);
|
||||
}
|
||||
|
||||
/* Turn off core dumps but only if we're running outside of a container. */
|
||||
void disable_coredumps(void) {
|
||||
int r;
|
||||
|
||||
if (detect_container() > 0)
|
||||
return;
|
||||
|
||||
r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
|||
|
|
@ -191,3 +191,5 @@ int update_reboot_parameter_and_warn(const char *param);
|
|||
int version(void);
|
||||
|
||||
int str_verscmp(const char *s1, const char *s2);
|
||||
|
||||
void disable_coredumps(void);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "arp-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "unaligned.h"
|
||||
#include "util.h"
|
||||
|
||||
int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
|
||||
|
|
@ -50,12 +51,12 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
|
|||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
|
||||
/* Sender Hardware Address must be different from our own */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_ne32(ð_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */
|
||||
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
|
||||
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */
|
||||
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_ne16(ð_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */
|
||||
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */
|
||||
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ struct sd_dhcp_route {
|
|||
struct in_addr dst_addr;
|
||||
struct in_addr gw_addr;
|
||||
unsigned char dst_prefixlen;
|
||||
|
||||
uint8_t option;
|
||||
};
|
||||
|
||||
struct sd_dhcp_raw_option {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "dhcp-internal.h"
|
||||
#include "fd-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
|
||||
uint32_t xid, const uint8_t *mac_addr,
|
||||
|
|
@ -72,13 +73,13 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
|
|||
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))), /* A <- 4 bytes of client's MAC */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(ð_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */
|
||||
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
|
||||
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */
|
||||
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
|
||||
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(ð_mac->ether_addr_octet[4])), /* A <- remainder of client's MAC */
|
||||
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */
|
||||
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
|
||||
|
|
|
|||
|
|
@ -29,25 +29,65 @@
|
|||
#include "macro.h"
|
||||
#include "sparse-endian.h"
|
||||
|
||||
/* Common option header */
|
||||
typedef struct DHCP6Option {
|
||||
be16_t code;
|
||||
be16_t len;
|
||||
uint8_t data[];
|
||||
} _packed_ DHCP6Option;
|
||||
|
||||
/* Address option */
|
||||
struct iaaddr {
|
||||
struct in6_addr address;
|
||||
be32_t lifetime_preferred;
|
||||
be32_t lifetime_valid;
|
||||
} _packed_;
|
||||
|
||||
/* Prefix Delegation Prefix option */
|
||||
struct iapdprefix {
|
||||
be32_t lifetime_preferred;
|
||||
be32_t lifetime_valid;
|
||||
uint8_t prefixlen;
|
||||
struct in6_addr address;
|
||||
} _packed_;
|
||||
|
||||
typedef struct DHCP6Address DHCP6Address;
|
||||
|
||||
struct DHCP6Address {
|
||||
LIST_FIELDS(DHCP6Address, addresses);
|
||||
|
||||
struct {
|
||||
struct in6_addr address;
|
||||
be32_t lifetime_preferred;
|
||||
be32_t lifetime_valid;
|
||||
} iaaddr _packed_;
|
||||
union {
|
||||
struct iaaddr iaaddr;
|
||||
struct iapdprefix iapdprefix;
|
||||
};
|
||||
};
|
||||
|
||||
/* Non-temporary Address option */
|
||||
struct ia_na {
|
||||
be32_t id;
|
||||
be32_t lifetime_t1;
|
||||
be32_t lifetime_t2;
|
||||
} _packed_;
|
||||
|
||||
/* Prefix Delegation option */
|
||||
struct ia_pd {
|
||||
be32_t id;
|
||||
be32_t lifetime_t1;
|
||||
be32_t lifetime_t2;
|
||||
} _packed_;
|
||||
|
||||
/* Temporary Address option */
|
||||
struct ia_ta {
|
||||
be32_t id;
|
||||
} _packed_;
|
||||
|
||||
struct DHCP6IA {
|
||||
uint16_t type;
|
||||
struct {
|
||||
be32_t id;
|
||||
be32_t lifetime_t1;
|
||||
be32_t lifetime_t2;
|
||||
} _packed_;
|
||||
union {
|
||||
struct ia_na ia_na;
|
||||
struct ia_pd ia_pd;
|
||||
struct ia_ta ia_ta;
|
||||
};
|
||||
sd_event_source *timeout_t1;
|
||||
sd_event_source *timeout_t2;
|
||||
|
||||
|
|
@ -62,11 +102,12 @@ typedef struct DHCP6IA DHCP6IA;
|
|||
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
||||
size_t optlen, const void *optval);
|
||||
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
|
||||
int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd);
|
||||
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
|
||||
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
|
||||
size_t *optlen, uint8_t **optvalue);
|
||||
int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
||||
DHCP6IA *ia);
|
||||
int dhcp6_option_parse_status(DHCP6Option *option);
|
||||
int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
|
||||
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
||||
struct in6_addr **addrs, size_t count,
|
||||
size_t *allocated);
|
||||
|
|
|
|||
|
|
@ -36,8 +36,10 @@ struct sd_dhcp6_lease {
|
|||
bool rapid_commit;
|
||||
|
||||
DHCP6IA ia;
|
||||
DHCP6IA pd;
|
||||
|
||||
DHCP6Address *addr_iter;
|
||||
DHCP6Address *prefix_iter;
|
||||
|
||||
struct in6_addr *dns;
|
||||
size_t dns_count;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp6-internal.h"
|
||||
#include "dhcp6-lease-internal.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "dns-domain.h"
|
||||
#include "sparse-endian.h"
|
||||
|
|
@ -35,14 +36,27 @@
|
|||
#include "unaligned.h"
|
||||
#include "util.h"
|
||||
|
||||
#define DHCP6_OPTION_IA_NA_LEN 12
|
||||
#define DHCP6_OPTION_IA_TA_LEN 4
|
||||
typedef struct DHCP6StatusOption {
|
||||
struct DHCP6Option option;
|
||||
be16_t status;
|
||||
char msg[];
|
||||
} _packed_ DHCP6StatusOption;
|
||||
|
||||
typedef struct DHCP6Option {
|
||||
be16_t code;
|
||||
be16_t len;
|
||||
uint8_t data[];
|
||||
} _packed_ DHCP6Option;
|
||||
typedef struct DHCP6AddressOption {
|
||||
struct DHCP6Option option;
|
||||
struct iaaddr iaaddr;
|
||||
uint8_t options[];
|
||||
} _packed_ DHCP6AddressOption;
|
||||
|
||||
typedef struct DHCP6PDPrefixOption {
|
||||
struct DHCP6Option option;
|
||||
struct iapdprefix iapdprefix;
|
||||
uint8_t options[];
|
||||
} _packed_ DHCP6PDPrefixOption;
|
||||
|
||||
#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
|
||||
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
|
||||
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
|
||||
|
||||
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
|
||||
size_t optlen) {
|
||||
|
|
@ -85,7 +99,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
|||
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
|
||||
uint16_t len;
|
||||
uint8_t *ia_hdr;
|
||||
size_t ia_buflen, ia_addrlen = 0;
|
||||
size_t iaid_offset, ia_buflen, ia_addrlen = 0;
|
||||
DHCP6Address *addr;
|
||||
int r;
|
||||
|
||||
|
|
@ -94,10 +108,12 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
|
|||
switch (ia->type) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
len = DHCP6_OPTION_IA_NA_LEN;
|
||||
iaid_offset = offsetof(DHCP6IA, ia_na);
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
len = DHCP6_OPTION_IA_TA_LEN;
|
||||
iaid_offset = offsetof(DHCP6IA, ia_ta);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -113,7 +129,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
|
|||
*buf += sizeof(DHCP6Option);
|
||||
*buflen -= sizeof(DHCP6Option);
|
||||
|
||||
memcpy(*buf, &ia->id, len);
|
||||
memcpy(*buf, (char*) ia + iaid_offset, len);
|
||||
|
||||
*buf += len;
|
||||
*buflen -= len;
|
||||
|
|
@ -166,6 +182,42 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
|
|||
return r;
|
||||
}
|
||||
|
||||
int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) {
|
||||
DHCP6Option *option = (DHCP6Option *)buf;
|
||||
size_t i = sizeof(*option) + sizeof(pd->ia_pd);
|
||||
DHCP6Address *prefix;
|
||||
|
||||
assert_return(buf, -EINVAL);
|
||||
assert_return(pd, -EINVAL);
|
||||
assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
|
||||
|
||||
if (len < i)
|
||||
return -ENOBUFS;
|
||||
|
||||
option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
|
||||
|
||||
memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
|
||||
|
||||
LIST_FOREACH(addresses, prefix, pd->addresses) {
|
||||
DHCP6PDPrefixOption *prefix_opt;
|
||||
|
||||
if (len < i + sizeof(*prefix_opt))
|
||||
return -ENOBUFS;
|
||||
|
||||
prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
|
||||
prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
|
||||
prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
|
||||
|
||||
memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix,
|
||||
sizeof(struct iapdprefix));
|
||||
|
||||
i += sizeof(*prefix_opt);
|
||||
}
|
||||
|
||||
option->len = htobe16(i - sizeof(*option));
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
|
||||
DHCP6Option *option = (DHCP6Option*) *buf;
|
||||
|
|
@ -212,35 +264,147 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
||||
DHCP6IA *ia) {
|
||||
int r;
|
||||
uint16_t opt, status;
|
||||
size_t optlen;
|
||||
size_t iaaddr_offset;
|
||||
int dhcp6_option_parse_status(DHCP6Option *option) {
|
||||
DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
|
||||
|
||||
if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt))
|
||||
return -ENOBUFS;
|
||||
|
||||
return be16toh(statusopt->status);
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
|
||||
uint32_t *lifetime_valid) {
|
||||
DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
|
||||
DHCP6Address *addr;
|
||||
uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0;
|
||||
uint32_t lt_valid, lt_pref;
|
||||
int r;
|
||||
|
||||
if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option))
|
||||
return -ENOBUFS;
|
||||
|
||||
lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
|
||||
lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
|
||||
|
||||
if (lt_valid == 0 || lt_pref > lt_valid) {
|
||||
log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
|
||||
lt_pref, lt_valid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) {
|
||||
r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options);
|
||||
if (r != 0)
|
||||
return r < 0 ? r: 0;
|
||||
}
|
||||
|
||||
addr = new0(DHCP6Address, 1);
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, addr);
|
||||
memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
|
||||
|
||||
LIST_PREPEND(addresses, ia->addresses, addr);
|
||||
|
||||
*lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
|
||||
uint32_t *lifetime_valid) {
|
||||
DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
|
||||
DHCP6Address *prefix;
|
||||
uint32_t lt_valid, lt_pref;
|
||||
int r;
|
||||
|
||||
if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option))
|
||||
return -ENOBUFS;
|
||||
|
||||
lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
|
||||
lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
|
||||
|
||||
if (lt_valid == 0 || lt_pref > lt_valid) {
|
||||
log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
|
||||
lt_pref, lt_valid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) {
|
||||
r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options);
|
||||
if (r != 0)
|
||||
return r < 0 ? r: 0;
|
||||
}
|
||||
|
||||
prefix = new0(DHCP6Address, 1);
|
||||
if (!prefix)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, prefix);
|
||||
memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
|
||||
|
||||
LIST_PREPEND(addresses, ia->addresses, prefix);
|
||||
|
||||
*lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
|
||||
uint16_t iatype, optlen;
|
||||
size_t i, len;
|
||||
int r = 0, status;
|
||||
uint16_t opt;
|
||||
size_t iaaddr_offset;
|
||||
uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
|
||||
|
||||
assert_return(ia, -EINVAL);
|
||||
assert_return(!ia->addresses, -EINVAL);
|
||||
|
||||
iatype = be16toh(iaoption->code);
|
||||
len = be16toh(iaoption->len);
|
||||
|
||||
switch (iatype) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
|
||||
if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
|
||||
sizeof(addr->iaaddr)) {
|
||||
if (len < DHCP6_OPTION_IA_NA_LEN) {
|
||||
r = -ENOBUFS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
|
||||
memcpy(&ia->id, *buf, iaaddr_offset);
|
||||
memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
|
||||
|
||||
lt_t1 = be32toh(ia->lifetime_t1);
|
||||
lt_t2 = be32toh(ia->lifetime_t2);
|
||||
lt_t1 = be32toh(ia->ia_na.lifetime_t1);
|
||||
lt_t2 = be32toh(ia->ia_na.lifetime_t2);
|
||||
|
||||
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
|
||||
log_dhcp6_client(client, "IA T1 %ds > T2 %ds",
|
||||
log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
|
||||
lt_t1, lt_t2);
|
||||
r = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
|
||||
if (len < sizeof(ia->ia_pd)) {
|
||||
r = -ENOBUFS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
iaaddr_offset = sizeof(ia->ia_pd);
|
||||
memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
|
||||
|
||||
lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
|
||||
lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
|
||||
|
||||
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
|
||||
log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
|
||||
lt_t1, lt_t2);
|
||||
r = -EINVAL;
|
||||
goto error;
|
||||
|
|
@ -249,17 +413,13 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
|||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
|
||||
sizeof(addr->iaaddr)) {
|
||||
if (len < DHCP6_OPTION_IA_TA_LEN) {
|
||||
r = -ENOBUFS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
|
||||
memcpy(&ia->id, *buf, iaaddr_offset);
|
||||
|
||||
ia->lifetime_t1 = 0;
|
||||
ia->lifetime_t2 = 0;
|
||||
memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
|
||||
|
||||
break;
|
||||
|
||||
|
|
@ -269,48 +429,63 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
|||
}
|
||||
|
||||
ia->type = iatype;
|
||||
i = iaaddr_offset;
|
||||
|
||||
*buflen -= iaaddr_offset;
|
||||
*buf += iaaddr_offset;
|
||||
while (i < len) {
|
||||
DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
|
||||
|
||||
while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
|
||||
if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) {
|
||||
r = -ENOBUFS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
opt = be16toh(option->code);
|
||||
optlen = be16toh(option->len);
|
||||
|
||||
switch (opt) {
|
||||
case SD_DHCP6_OPTION_IAADDR:
|
||||
|
||||
addr = new0(DHCP6Address, 1);
|
||||
if (!addr) {
|
||||
r = -ENOMEM;
|
||||
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
|
||||
log_dhcp6_client(client, "IA Address option not in IA NA or TA option");
|
||||
r = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
LIST_INIT(addresses, addr);
|
||||
r = dhcp6_option_parse_address(option, ia, <_valid);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr));
|
||||
if (lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
|
||||
lt_valid = be32toh(addr->iaaddr.lifetime_valid);
|
||||
lt_pref = be32toh(addr->iaaddr.lifetime_valid);
|
||||
break;
|
||||
|
||||
if (!lt_valid || lt_pref > lt_valid) {
|
||||
log_dhcp6_client(client, "IA preferred %ds > valid %ds",
|
||||
lt_pref, lt_valid);
|
||||
free(addr);
|
||||
} else {
|
||||
LIST_PREPEND(addresses, ia->addresses, addr);
|
||||
if (lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
case SD_DHCP6_OPTION_IA_PD_PREFIX:
|
||||
|
||||
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) {
|
||||
log_dhcp6_client(client, "IA PD Prefix option not in IA PD option");
|
||||
r = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_pdprefix(option, ia, <_valid);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
if (lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_STATUS_CODE:
|
||||
if (optlen < sizeof(status))
|
||||
break;
|
||||
|
||||
status = (*buf)[0] << 8 | (*buf)[1];
|
||||
status = dhcp6_option_parse_status(option);
|
||||
if (status) {
|
||||
log_dhcp6_client(client, "IA status %d",
|
||||
status);
|
||||
|
||||
dhcp6_lease_free_ia(ia);
|
||||
|
||||
r = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
|
@ -322,30 +497,41 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
|||
break;
|
||||
}
|
||||
|
||||
*buflen -= optlen;
|
||||
*buf += optlen;
|
||||
i += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
if (r == -ENOMSG)
|
||||
r = 0;
|
||||
switch(iatype) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
|
||||
lt_t1 = lt_min / 2;
|
||||
lt_t2 = lt_min / 10 * 8;
|
||||
ia->ia_na.lifetime_t1 = htobe32(lt_t1);
|
||||
ia->ia_na.lifetime_t2 = htobe32(lt_t2);
|
||||
|
||||
if (!ia->lifetime_t1 && !ia->lifetime_t2) {
|
||||
lt_t1 = lt_min / 2;
|
||||
lt_t2 = lt_min / 10 * 8;
|
||||
ia->lifetime_t1 = htobe32(lt_t1);
|
||||
ia->lifetime_t2 = htobe32(lt_t2);
|
||||
log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero",
|
||||
lt_t1, lt_t2);
|
||||
}
|
||||
|
||||
log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero",
|
||||
lt_t1, lt_t2);
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
|
||||
lt_t1 = lt_min / 2;
|
||||
lt_t2 = lt_min / 10 * 8;
|
||||
ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
|
||||
ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
|
||||
|
||||
log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero",
|
||||
lt_t1, lt_t2);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (*buflen)
|
||||
r = -ENOMSG;
|
||||
|
||||
error:
|
||||
*buf += *buflen;
|
||||
*buflen = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ struct DHCP6Message {
|
|||
} _packed_;
|
||||
be32_t transaction_id;
|
||||
};
|
||||
uint8_t options[];
|
||||
} _packed_;
|
||||
|
||||
typedef struct DHCP6Message DHCP6Message;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/if.h>
|
||||
#include <netinet/ether.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
|
|
|
|||
|
|
@ -1265,9 +1265,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_
|
|||
if (!lease->have_subnet_mask) {
|
||||
r = dhcp_lease_set_default_subnet_mask(lease);
|
||||
if (r < 0) {
|
||||
log_dhcp_client(client, "received lease lacks subnet "
|
||||
"mask, and a fallback one can not be "
|
||||
"generated, ignoring");
|
||||
log_dhcp_client(client,
|
||||
"received lease lacks subnet mask, "
|
||||
"and a fallback one cannot be generated, ignoring");
|
||||
return -ENOMSG;
|
||||
}
|
||||
}
|
||||
|
|
@ -1336,9 +1336,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le
|
|||
if (lease->subnet_mask == INADDR_ANY) {
|
||||
r = dhcp_lease_set_default_subnet_mask(lease);
|
||||
if (r < 0) {
|
||||
log_dhcp_client(client, "received lease lacks subnet "
|
||||
"mask, and a fallback one can not be "
|
||||
"generated, ignoring");
|
||||
log_dhcp_client(client,
|
||||
"received lease lacks subnet mask, "
|
||||
"and a fallback one cannot be generated, ignoring");
|
||||
return -ENOMSG;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -473,6 +473,7 @@ static int lease_parse_routes(
|
|||
struct sd_dhcp_route *route = *routes + *routes_size;
|
||||
int r;
|
||||
|
||||
route->option = SD_DHCP_OPTION_STATIC_ROUTE;
|
||||
r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
|
||||
if (r < 0) {
|
||||
log_debug("Failed to determine destination prefix length from class based IP, ignoring");
|
||||
|
|
@ -516,6 +517,7 @@ static int lease_parse_classless_routes(
|
|||
return -ENOMEM;
|
||||
|
||||
route = *routes + *routes_size;
|
||||
route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE;
|
||||
|
||||
dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
|
||||
route->dst_prefixlen = *option;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ struct sd_dhcp6_client {
|
|||
size_t mac_addr_len;
|
||||
uint16_t arp_type;
|
||||
DHCP6IA ia_na;
|
||||
DHCP6IA ia_pd;
|
||||
bool prefix_delegation;
|
||||
be32_t transaction_id;
|
||||
usec_t transaction_start;
|
||||
struct sd_dhcp6_lease *lease;
|
||||
|
|
@ -233,7 +235,8 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
|
|||
assert_return(client, -EINVAL);
|
||||
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
||||
|
||||
client->ia_na.id = htobe32(iaid);
|
||||
client->ia_na.ia_na.id = htobe32(iaid);
|
||||
client->ia_pd.ia_pd.id = htobe32(iaid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -283,6 +286,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
|
|||
case SD_DHCP6_OPTION_DOMAIN_LIST:
|
||||
case SD_DHCP6_OPTION_SNTP_SERVERS:
|
||||
case SD_DHCP6_OPTION_NTP_SERVER:
|
||||
case SD_DHCP6_OPTION_RAPID_COMMIT:
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -302,6 +306,14 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) {
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
client->prefix_delegation = delegation;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
|
|
@ -340,8 +352,6 @@ static int client_reset(sd_dhcp6_client *client) {
|
|||
client->receive_message =
|
||||
sd_event_source_unref(client->receive_message);
|
||||
|
||||
client->fd = safe_close(client->fd);
|
||||
|
||||
client->transaction_id = 0;
|
||||
client->transaction_start = 0;
|
||||
|
||||
|
|
@ -417,6 +427,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
|
|||
return r;
|
||||
}
|
||||
|
||||
if (client->prefix_delegation) {
|
||||
r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
opt += r;
|
||||
optlen -= r;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DHCP6_STATE_REQUEST:
|
||||
|
|
@ -443,6 +462,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
|
|||
return r;
|
||||
}
|
||||
|
||||
if (client->prefix_delegation) {
|
||||
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
opt += r;
|
||||
optlen -= r;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DHCP6_STATE_REBIND:
|
||||
|
|
@ -458,6 +486,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
|
|||
return r;
|
||||
}
|
||||
|
||||
if (client->prefix_delegation) {
|
||||
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
opt += r;
|
||||
optlen -= r;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DHCP6_STATE_STOPPED:
|
||||
|
|
@ -713,16 +750,20 @@ error:
|
|||
|
||||
static int client_ensure_iaid(sd_dhcp6_client *client) {
|
||||
int r;
|
||||
be32_t iaid;
|
||||
|
||||
assert(client);
|
||||
|
||||
if (client->ia_na.id)
|
||||
if (client->ia_na.ia_na.id)
|
||||
return 0;
|
||||
|
||||
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
|
||||
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
client->ia_na.ia_na.id = iaid;
|
||||
client->ia_pd.ia_pd.id = iaid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -731,23 +772,35 @@ static int client_parse_message(
|
|||
DHCP6Message *message,
|
||||
size_t len,
|
||||
sd_dhcp6_lease *lease) {
|
||||
size_t pos = 0;
|
||||
int r;
|
||||
uint8_t *optval, *option, *id = NULL;
|
||||
uint16_t optcode, status;
|
||||
size_t optlen, id_len;
|
||||
bool clientid = false;
|
||||
be32_t iaid_lease;
|
||||
uint8_t *id = NULL;
|
||||
size_t id_len;
|
||||
uint32_t lt_t1 = ~0, lt_t2 = ~0;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(len >= sizeof(DHCP6Message));
|
||||
assert(lease);
|
||||
|
||||
option = (uint8_t *)message + sizeof(DHCP6Message);
|
||||
len -= sizeof(DHCP6Message);
|
||||
|
||||
while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
|
||||
&optval)) >= 0) {
|
||||
while (pos < len) {
|
||||
DHCP6Option *option = (DHCP6Option *)&message->options[pos];
|
||||
uint16_t optcode, optlen;
|
||||
int status;
|
||||
uint8_t *optval;
|
||||
be32_t iaid_lease;
|
||||
|
||||
if (len < offsetof(DHCP6Option, data) ||
|
||||
len < offsetof(DHCP6Option, data) + be16toh(option->len))
|
||||
return -ENOBUFS;
|
||||
|
||||
optcode = be16toh(option->code);
|
||||
optlen = be16toh(option->len);
|
||||
optval = option->data;
|
||||
|
||||
switch (optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
if (clientid) {
|
||||
|
|
@ -785,21 +838,21 @@ static int client_parse_message(
|
|||
if (optlen != 1)
|
||||
return -EINVAL;
|
||||
|
||||
r = dhcp6_lease_set_preference(lease, *optval);
|
||||
r = dhcp6_lease_set_preference(lease, optval[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_STATUS_CODE:
|
||||
if (optlen < 2)
|
||||
return -EINVAL;
|
||||
|
||||
status = optval[0] << 8 | optval[1];
|
||||
status = dhcp6_option_parse_status(option);
|
||||
if (status) {
|
||||
log_dhcp6_client(client, "%s Status %s",
|
||||
dhcp6_message_type_to_string(message->type),
|
||||
dhcp6_message_status_to_string(status));
|
||||
dhcp6_lease_free_ia(&lease->ia);
|
||||
dhcp6_lease_free_ia(&lease->pd);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -812,8 +865,7 @@ static int client_parse_message(
|
|||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
|
||||
&lease->ia);
|
||||
r = dhcp6_option_parse_ia(option, &lease->ia);
|
||||
if (r < 0 && r != -ENOMSG)
|
||||
return r;
|
||||
|
||||
|
|
@ -821,12 +873,45 @@ static int client_parse_message(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (client->ia_na.id != iaid_lease) {
|
||||
log_dhcp6_client(client, "%s has wrong IAID",
|
||||
if (client->ia_na.ia_na.id != iaid_lease) {
|
||||
log_dhcp6_client(client, "%s has wrong IAID for IA NA",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (lease->ia.addresses) {
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
log_dhcp6_client(client, "Information request ignoring IA PD option");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(option, &lease->pd);
|
||||
if (r < 0 && r != -ENOMSG)
|
||||
return r;
|
||||
|
||||
r = dhcp6_lease_get_iaid(lease, &iaid_lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (client->ia_pd.ia_pd.id != iaid_lease) {
|
||||
log_dhcp6_client(client, "%s has wrong IAID for IA PD",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (lease->pd.addresses) {
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_RAPID_COMMIT:
|
||||
|
|
@ -865,12 +950,10 @@ static int client_parse_message(
|
|||
break;
|
||||
}
|
||||
|
||||
pos += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
if (r == -ENOMSG)
|
||||
r = 0;
|
||||
|
||||
if (r < 0 || !clientid) {
|
||||
if (!clientid) {
|
||||
log_dhcp6_client(client, "%s has incomplete options",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
|
|
@ -881,9 +964,20 @@ static int client_parse_message(
|
|||
if (r < 0)
|
||||
log_dhcp6_client(client, "%s has no server id",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return r;
|
||||
}
|
||||
|
||||
return r;
|
||||
if (lease->ia.addresses) {
|
||||
lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
|
||||
lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
|
||||
}
|
||||
|
||||
if (lease->pd.addresses) {
|
||||
lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
|
||||
lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
|
||||
|
|
@ -1096,6 +1190,24 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!client->receive_message) {
|
||||
r = sd_event_add_io(client->event, &client->receive_message,
|
||||
client->fd, EPOLLIN, client_receive_message,
|
||||
client);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_priority(client->receive_message,
|
||||
client->event_priority);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_description(client->receive_message,
|
||||
"dhcp6-receive-message");
|
||||
if (r < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case DHCP6_STATE_STOPPED:
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
|
|
@ -1121,17 +1233,17 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
|
||||
case DHCP6_STATE_BOUND:
|
||||
|
||||
if (client->lease->ia.lifetime_t1 == 0xffffffff ||
|
||||
client->lease->ia.lifetime_t2 == 0xffffffff) {
|
||||
if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff ||
|
||||
client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) {
|
||||
|
||||
log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
|
||||
be32toh(client->lease->ia.lifetime_t1),
|
||||
be32toh(client->lease->ia.lifetime_t2));
|
||||
be32toh(client->lease->ia.ia_na.lifetime_t1),
|
||||
be32toh(client->lease->ia.ia_na.lifetime_t2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
|
||||
timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC);
|
||||
|
||||
log_dhcp6_client(client, "T1 expires in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
|
||||
|
|
@ -1142,18 +1254,18 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
10 * USEC_PER_SEC, client_timeout_t1,
|
||||
client);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
|
||||
client->event_priority);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto error;
|
||||
|
||||
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
|
||||
timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC);
|
||||
|
||||
log_dhcp6_client(client, "T2 expires in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
|
||||
|
|
@ -1164,16 +1276,16 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
10 * USEC_PER_SEC, client_timeout_t2,
|
||||
client);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
|
||||
client->event_priority);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto error;
|
||||
|
||||
client->state = state;
|
||||
|
||||
|
|
@ -1187,18 +1299,22 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
|
||||
client);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_priority(client->timeout_resend,
|
||||
client->event_priority);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
client_reset(client);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
|
||||
|
|
@ -1206,6 +1322,8 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
|
|||
|
||||
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
|
||||
|
||||
client->fd = safe_close(client->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1239,33 +1357,19 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
|
||||
if (r < 0) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
if (client->fd < 0) {
|
||||
r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
|
||||
if (r < 0) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
|
||||
return log_dhcp6_client_errno(client, r,
|
||||
"Failed to bind to UDP socket at address %s: %m", strna(p));
|
||||
(void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
|
||||
return log_dhcp6_client_errno(client, r,
|
||||
"Failed to bind to UDP socket at address %s: %m", strna(p));
|
||||
}
|
||||
|
||||
client->fd = r;
|
||||
}
|
||||
|
||||
client->fd = r;
|
||||
|
||||
r = sd_event_add_io(client->event, &client->receive_message,
|
||||
client->fd, EPOLLIN, client_receive_message,
|
||||
client);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_priority(client->receive_message,
|
||||
client->event_priority);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = sd_event_source_set_description(client->receive_message,
|
||||
"dhcp6-receive-message");
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
if (client->information_request)
|
||||
state = DHCP6_STATE_INFORMATION_REQUEST;
|
||||
|
||||
|
|
@ -1274,10 +1378,6 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
|
|||
"Managed");
|
||||
|
||||
return client_start(client, state);
|
||||
|
||||
error:
|
||||
client_reset(client);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
|
||||
|
|
@ -1337,6 +1437,8 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
|
|||
|
||||
client_reset(client);
|
||||
|
||||
client->fd = safe_close(client->fd);
|
||||
|
||||
sd_dhcp6_client_detach_event(client);
|
||||
|
||||
free(client->req_opts);
|
||||
|
|
@ -1356,6 +1458,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
|
|||
|
||||
client->n_ref = 1;
|
||||
client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
|
||||
client->ia_pd.type = SD_DHCP6_OPTION_IA_PD;
|
||||
client->ifindex = -1;
|
||||
client->fd = -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
|
|||
valid = t;
|
||||
}
|
||||
|
||||
t = be32toh(ia->lifetime_t2);
|
||||
t = be32toh(ia->ia_na.lifetime_t2);
|
||||
if (t > valid)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
|
|||
assert_return(lease, -EINVAL);
|
||||
assert_return(iaid, -EINVAL);
|
||||
|
||||
*iaid = lease->ia.id;
|
||||
*iaid = lease->ia.ia_na.id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -178,6 +178,37 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
|
|||
lease->addr_iter = lease->ia.addresses;
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
|
||||
uint8_t *prefix_len,
|
||||
uint32_t *lifetime_preferred,
|
||||
uint32_t *lifetime_valid) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(prefix, -EINVAL);
|
||||
assert_return(prefix_len, -EINVAL);
|
||||
assert_return(lifetime_preferred, -EINVAL);
|
||||
assert_return(lifetime_valid, -EINVAL);
|
||||
|
||||
if (!lease->prefix_iter)
|
||||
return -ENOMSG;
|
||||
|
||||
memcpy(prefix, &lease->prefix_iter->iapdprefix.address,
|
||||
sizeof(struct in6_addr));
|
||||
*prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
|
||||
*lifetime_preferred =
|
||||
be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
|
||||
*lifetime_valid =
|
||||
be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
|
||||
|
||||
lease->prefix_iter = lease->prefix_iter->addresses_next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
|
||||
if (lease)
|
||||
lease->prefix_iter = lease->pd.addresses;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
|
||||
|
|
@ -384,6 +415,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
|
|||
|
||||
free(lease->serverid);
|
||||
dhcp6_lease_free_ia(&lease->ia);
|
||||
dhcp6_lease_free_ia(&lease->pd);
|
||||
|
||||
free(lease->dns);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
#include "sd-ipv4acd.h"
|
||||
#include "sd-ipv4ll.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ struct sd_event_source {
|
|||
uint32_t events;
|
||||
uint32_t revents;
|
||||
bool registered:1;
|
||||
bool owned:1;
|
||||
} io;
|
||||
struct {
|
||||
sd_event_time_handler_t callback;
|
||||
|
|
@ -242,8 +243,14 @@ struct sd_event {
|
|||
unsigned delays[sizeof(usec_t) * 8];
|
||||
};
|
||||
|
||||
static thread_local sd_event *default_event = NULL;
|
||||
|
||||
static void source_disconnect(sd_event_source *s);
|
||||
|
||||
static sd_event *event_resolve(sd_event *e) {
|
||||
return e == SD_EVENT_DEFAULT ? default_event : e;
|
||||
}
|
||||
|
||||
static int pending_prioq_compare(const void *a, const void *b) {
|
||||
const sd_event_source *x = a, *y = b;
|
||||
|
||||
|
|
@ -452,6 +459,8 @@ _public_ int sd_event_new(sd_event** ret) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
e->epoll_fd = fd_move_above_stdio(e->epoll_fd);
|
||||
|
||||
if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
|
||||
log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s.");
|
||||
e->profile_delays = true;
|
||||
|
|
@ -690,7 +699,7 @@ static int event_make_signal_data(
|
|||
return 0;
|
||||
}
|
||||
|
||||
d->fd = r;
|
||||
d->fd = fd_move_above_stdio(r);
|
||||
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = d;
|
||||
|
|
@ -885,6 +894,10 @@ static void source_free(sd_event_source *s) {
|
|||
assert(s);
|
||||
|
||||
source_disconnect(s);
|
||||
|
||||
if (s->type == SOURCE_IO && s->io.owned)
|
||||
safe_close(s->io.fd);
|
||||
|
||||
free(s->description);
|
||||
free(s);
|
||||
}
|
||||
|
|
@ -969,6 +982,7 @@ _public_ int sd_event_add_io(
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(fd >= 0, -EBADF);
|
||||
assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
|
||||
assert_return(callback, -EINVAL);
|
||||
|
|
@ -1035,6 +1049,8 @@ static int event_setup_timer_fd(
|
|||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd = fd_move_above_stdio(fd);
|
||||
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = d;
|
||||
|
||||
|
|
@ -1069,6 +1085,7 @@ _public_ int sd_event_add_time(
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(accuracy != (uint64_t) -1, -EINVAL);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
|
@ -1151,6 +1168,7 @@ _public_ int sd_event_add_signal(
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(SIGNAL_VALID(sig), -EINVAL);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
|
@ -1211,6 +1229,7 @@ _public_ int sd_event_add_child(
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(pid > 1, -EINVAL);
|
||||
assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
|
||||
assert_return(options != 0, -EINVAL);
|
||||
|
|
@ -1268,6 +1287,7 @@ _public_ int sd_event_add_defer(
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(callback, -EINVAL);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
|
@ -1302,6 +1322,7 @@ _public_ int sd_event_add_post(
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(callback, -EINVAL);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
|
@ -1340,6 +1361,7 @@ _public_ int sd_event_add_exit(
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(callback, -EINVAL);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
|
@ -1485,6 +1507,21 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_event_source_get_io_fd_own(sd_event_source *s) {
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(s->type == SOURCE_IO, -EDOM);
|
||||
|
||||
return s->io.owned;
|
||||
}
|
||||
|
||||
_public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) {
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(s->type == SOURCE_IO, -EDOM);
|
||||
|
||||
s->io.owned = own;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) {
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(events, -EINVAL);
|
||||
|
|
@ -2453,6 +2490,7 @@ _public_ int sd_event_prepare(sd_event *e) {
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
|
||||
|
|
@ -2510,6 +2548,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
|
|||
int r, m, i;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
|
||||
|
|
@ -2616,6 +2655,7 @@ _public_ int sd_event_dispatch(sd_event *e) {
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(e->state == SD_EVENT_PENDING, -EBUSY);
|
||||
|
|
@ -2657,6 +2697,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
|
||||
|
|
@ -2701,6 +2742,7 @@ _public_ int sd_event_loop(sd_event *e) {
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
|
||||
|
||||
|
|
@ -2722,6 +2764,7 @@ finish:
|
|||
_public_ int sd_event_get_fd(sd_event *e) {
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
return e->epoll_fd;
|
||||
|
|
@ -2729,6 +2772,7 @@ _public_ int sd_event_get_fd(sd_event *e) {
|
|||
|
||||
_public_ int sd_event_get_state(sd_event *e) {
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
return e->state;
|
||||
|
|
@ -2736,6 +2780,7 @@ _public_ int sd_event_get_state(sd_event *e) {
|
|||
|
||||
_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(code, -EINVAL);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
|
|
@ -2748,6 +2793,7 @@ _public_ int sd_event_get_exit_code(sd_event *e, int *code) {
|
|||
|
||||
_public_ int sd_event_exit(sd_event *e, int code) {
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
|
|
@ -2759,6 +2805,7 @@ _public_ int sd_event_exit(sd_event *e, int code) {
|
|||
|
||||
_public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(usec, -EINVAL);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
|
|
@ -2783,8 +2830,6 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
|
|||
}
|
||||
|
||||
_public_ int sd_event_default(sd_event **ret) {
|
||||
|
||||
static thread_local sd_event *default_event = NULL;
|
||||
sd_event *e = NULL;
|
||||
int r;
|
||||
|
||||
|
|
@ -2811,6 +2856,7 @@ _public_ int sd_event_default(sd_event **ret) {
|
|||
#if 0 /* NM_IGNORED */
|
||||
_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(tid, -EINVAL);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
|
|
@ -2826,6 +2872,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
|
|||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
if (e->watchdog == !!b)
|
||||
|
|
@ -2876,6 +2923,7 @@ fail:
|
|||
|
||||
_public_ int sd_event_get_watchdog(sd_event *e) {
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
return e->watchdog;
|
||||
|
|
@ -2883,6 +2931,7 @@ _public_ int sd_event_get_watchdog(sd_event *e) {
|
|||
|
||||
_public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) {
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(e = event_resolve(e), -ENOPKG);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
*ret = e->iteration;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "nm-sd-adapt.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-dhcp6-lease.h"
|
||||
|
|
@ -64,6 +65,8 @@ enum {
|
|||
|
||||
SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
|
||||
SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
|
||||
SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, prefix delegation */
|
||||
SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, prefix delegation */
|
||||
|
||||
SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
|
||||
|
||||
|
|
@ -116,6 +119,8 @@ int sd_dhcp6_client_get_information_request(
|
|||
int sd_dhcp6_client_set_request_option(
|
||||
sd_dhcp6_client *client,
|
||||
uint16_t option);
|
||||
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
|
||||
bool delegation);
|
||||
|
||||
int sd_dhcp6_client_get_lease(
|
||||
sd_dhcp6_client *client,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
|
|||
struct in6_addr *addr,
|
||||
uint32_t *lifetime_preferred,
|
||||
uint32_t *lifetime_valid);
|
||||
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease);
|
||||
int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
|
||||
uint8_t *prefix_len,
|
||||
uint32_t *lifetime_preferred,
|
||||
uint32_t *lifetime_valid);
|
||||
|
||||
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs);
|
||||
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <sys/epoll.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "_sd-common.h"
|
||||
|
||||
|
|
@ -40,6 +41,8 @@
|
|||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
#define SD_EVENT_DEFAULT ((sd_event *) 1)
|
||||
|
||||
typedef struct sd_event sd_event;
|
||||
typedef struct sd_event_source sd_event_source;
|
||||
|
||||
|
|
@ -124,6 +127,8 @@ int sd_event_source_get_enabled(sd_event_source *s, int *enabled);
|
|||
int sd_event_source_set_enabled(sd_event_source *s, int enabled);
|
||||
int sd_event_source_get_io_fd(sd_event_source *s);
|
||||
int sd_event_source_set_io_fd(sd_event_source *s, int fd);
|
||||
int sd_event_source_get_io_fd_own(sd_event_source *s);
|
||||
int sd_event_source_set_io_fd_own(sd_event_source *s, int own);
|
||||
int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events);
|
||||
int sd_event_source_set_io_events(sd_event_source *s, uint32_t events);
|
||||
int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue