systemd: update code from upstream (2019-07-04)

This is a direct dump from systemd git.

======

SYSTEMD_DIR=../systemd
COMMIT=52d8bcd131e7f3c9b7629412302e1ea46ddb70ca

(
  cd "$SYSTEMD_DIR"
  git checkout "$COMMIT"
  git reset --hard
  git clean -fdx
)

git ls-files -z :/src/systemd/src/ \
                :/shared/systemd/src/ \
                :/shared/nm-utils/unaligned.h | \
  xargs -0 rm -f

nm_copy_sd_shared() {
    mkdir -p "./shared/systemd/$(dirname "$1")"
    cp "$SYSTEMD_DIR/$1" "./shared/systemd/$1"
}

nm_copy_sd_core() {
    mkdir -p "./src/systemd/$(dirname "$1")"
    cp "$SYSTEMD_DIR/$1" "./src/systemd/$1"
}

nm_copy_sd_nmutils() {
    mkdir -p "./shared/nm-utils/"
    cp "$SYSTEMD_DIR/$1" "./shared/nm-utils/${1##*/}"
}

nm_copy_sd_core "src/libsystemd-network/arp-util.c"
nm_copy_sd_core "src/libsystemd-network/arp-util.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-lease-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-network.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-option.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-packet.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-protocol.h"
nm_copy_sd_core "src/libsystemd-network/dhcp6-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp6-lease-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp6-network.c"
nm_copy_sd_core "src/libsystemd-network/dhcp6-option.c"
nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.h"
nm_copy_sd_core "src/libsystemd-network/lldp-internal.h"
nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.c"
nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.h"
nm_copy_sd_core "src/libsystemd-network/lldp-network.c"
nm_copy_sd_core "src/libsystemd-network/lldp-network.h"
nm_copy_sd_core "src/libsystemd-network/network-internal.c"
nm_copy_sd_core "src/libsystemd-network/network-internal.h"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp-client.c"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp-lease.c"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-client.c"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-lease.c"
nm_copy_sd_core "src/libsystemd-network/sd-ipv4acd.c"
nm_copy_sd_core "src/libsystemd-network/sd-ipv4ll.c"
nm_copy_sd_core "src/libsystemd-network/sd-lldp.c"
nm_copy_sd_core "src/libsystemd/sd-event/event-source.h"
nm_copy_sd_core "src/libsystemd/sd-event/event-util.c"
nm_copy_sd_core "src/libsystemd/sd-event/event-util.h"
nm_copy_sd_core "src/libsystemd/sd-event/sd-event.c"
nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.c"
nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.h"
nm_copy_sd_core "src/libsystemd/sd-id128/sd-id128.c"
nm_copy_sd_core "src/shared/dns-domain.c"
nm_copy_sd_core "src/shared/dns-domain.h"
nm_copy_sd_core "src/systemd/_sd-common.h"
nm_copy_sd_core "src/systemd/sd-dhcp-client.h"
nm_copy_sd_core "src/systemd/sd-dhcp-lease.h"
nm_copy_sd_core "src/systemd/sd-dhcp6-client.h"
nm_copy_sd_core "src/systemd/sd-dhcp6-lease.h"
nm_copy_sd_core "src/systemd/sd-event.h"
nm_copy_sd_core "src/systemd/sd-id128.h"
nm_copy_sd_core "src/systemd/sd-ipv4acd.h"
nm_copy_sd_core "src/systemd/sd-ipv4ll.h"
nm_copy_sd_core "src/systemd/sd-lldp.h"
nm_copy_sd_core "src/systemd/sd-ndisc.h"
nm_copy_sd_nmutils "src/basic/unaligned.h"
nm_copy_sd_shared "src/basic/alloc-util.c"
nm_copy_sd_shared "src/basic/alloc-util.h"
nm_copy_sd_shared "src/basic/async.h"
nm_copy_sd_shared "src/basic/env-file.c"
nm_copy_sd_shared "src/basic/env-file.h"
nm_copy_sd_shared "src/basic/env-util.c"
nm_copy_sd_shared "src/basic/env-util.h"
nm_copy_sd_shared "src/basic/errno-util.h"
nm_copy_sd_shared "src/basic/escape.c"
nm_copy_sd_shared "src/basic/escape.h"
nm_copy_sd_shared "src/basic/ether-addr-util.c"
nm_copy_sd_shared "src/basic/ether-addr-util.h"
nm_copy_sd_shared "src/basic/extract-word.c"
nm_copy_sd_shared "src/basic/extract-word.h"
nm_copy_sd_shared "src/basic/fd-util.c"
nm_copy_sd_shared "src/basic/fd-util.h"
nm_copy_sd_shared "src/basic/fileio.c"
nm_copy_sd_shared "src/basic/fileio.h"
nm_copy_sd_shared "src/basic/format-util.c"
nm_copy_sd_shared "src/basic/format-util.h"
nm_copy_sd_shared "src/basic/fs-util.c"
nm_copy_sd_shared "src/basic/fs-util.h"
nm_copy_sd_shared "src/basic/hash-funcs.c"
nm_copy_sd_shared "src/basic/hash-funcs.h"
nm_copy_sd_shared "src/basic/hashmap.c"
nm_copy_sd_shared "src/basic/hashmap.h"
nm_copy_sd_shared "src/basic/hexdecoct.c"
nm_copy_sd_shared "src/basic/hexdecoct.h"
nm_copy_sd_shared "src/basic/hostname-util.c"
nm_copy_sd_shared "src/basic/hostname-util.h"
nm_copy_sd_shared "src/basic/in-addr-util.c"
nm_copy_sd_shared "src/basic/in-addr-util.h"
nm_copy_sd_shared "src/basic/io-util.c"
nm_copy_sd_shared "src/basic/io-util.h"
nm_copy_sd_shared "src/basic/list.h"
nm_copy_sd_shared "src/basic/log.h"
nm_copy_sd_shared "src/basic/macro.h"
nm_copy_sd_shared "src/basic/memory-util.c"
nm_copy_sd_shared "src/basic/memory-util.h"
nm_copy_sd_shared "src/basic/mempool.c"
nm_copy_sd_shared "src/basic/mempool.h"
nm_copy_sd_shared "src/basic/missing_fcntl.h"
nm_copy_sd_shared "src/basic/missing_socket.h"
nm_copy_sd_shared "src/basic/missing_stat.h"
nm_copy_sd_shared "src/basic/missing_type.h"
nm_copy_sd_shared "src/basic/parse-util.c"
nm_copy_sd_shared "src/basic/parse-util.h"
nm_copy_sd_shared "src/basic/path-util.c"
nm_copy_sd_shared "src/basic/path-util.h"
nm_copy_sd_shared "src/basic/prioq.c"
nm_copy_sd_shared "src/basic/prioq.h"
nm_copy_sd_shared "src/basic/process-util.c"
nm_copy_sd_shared "src/basic/process-util.h"
nm_copy_sd_shared "src/basic/random-util.c"
nm_copy_sd_shared "src/basic/random-util.h"
nm_copy_sd_shared "src/basic/set.h"
nm_copy_sd_shared "src/basic/signal-util.h"
nm_copy_sd_shared "src/basic/siphash24.h"
nm_copy_sd_shared "src/basic/socket-util.c"
nm_copy_sd_shared "src/basic/socket-util.h"
nm_copy_sd_shared "src/basic/sort-util.h"
nm_copy_sd_shared "src/basic/sparse-endian.h"
nm_copy_sd_shared "src/basic/stat-util.c"
nm_copy_sd_shared "src/basic/stat-util.h"
nm_copy_sd_shared "src/basic/stdio-util.h"
nm_copy_sd_shared "src/basic/string-table.c"
nm_copy_sd_shared "src/basic/string-table.h"
nm_copy_sd_shared "src/basic/string-util.c"
nm_copy_sd_shared "src/basic/string-util.h"
nm_copy_sd_shared "src/basic/strv.c"
nm_copy_sd_shared "src/basic/strv.h"
nm_copy_sd_shared "src/basic/time-util.c"
nm_copy_sd_shared "src/basic/time-util.h"
nm_copy_sd_shared "src/basic/tmpfile-util.c"
nm_copy_sd_shared "src/basic/tmpfile-util.h"
nm_copy_sd_shared "src/basic/umask-util.h"
nm_copy_sd_shared "src/basic/utf8.c"
nm_copy_sd_shared "src/basic/utf8.h"
nm_copy_sd_shared "src/basic/util.c"
nm_copy_sd_shared "src/basic/util.h"
This commit is contained in:
Beniamino Galvani 2019-07-04 18:13:17 +02:00
parent f61a12ee20
commit 55c47d4efa
39 changed files with 977 additions and 481 deletions

View file

@ -72,7 +72,7 @@ bool env_value_is_valid(const char *e) {
* either. Discounting the shortest possible variable name of
* length 1, the equal sign and trailing NUL this hence leaves
* ARG_MAX-3 as longest possible variable value. */
if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3)
if (strlen(e) > sc_arg_max() - 3)
return false;
return true;
@ -95,7 +95,7 @@ bool env_assignment_is_valid(const char *e) {
* be > ARG_MAX, hence the individual variable assignments
* cannot be either, but let's leave room for one trailing NUL
* byte. */
if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1)
if (strlen(e) > sc_arg_max() - 1)
return false;
return true;
@ -688,7 +688,7 @@ char **replace_env_argv(char **argv, char **env) {
if (e) {
int r;
r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);

View file

@ -4,10 +4,17 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include "macro.h"
#include "string.h"
static inline size_t sc_arg_max(void) {
long l = sysconf(_SC_ARG_MAX);
assert(l > 0);
return (size_t) l;
}
bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);

View file

@ -368,33 +368,78 @@ int cunescape(const char *s, UnescapeFlags flags, char **ret) {
return cunescape_length(s, strlen(s), flags, ret);
}
char *xescape(const char *s, const char *bad) {
char *r, *t;
char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
char *ans, *t, *prev, *prev2;
const char *f;
/* Escapes all chars in bad, in addition to \ and all special
* chars, in \xFF style escaping. May be reversed with
* cunescape(). */
/* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
* reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged.
* This corresponds to non-ASCII printable characters in pre-unicode encodings.
*
* If console_width is reached, output is truncated and "..." is appended. */
r = new(char, strlen(s) * 4 + 1);
if (!r)
if (console_width == 0)
return strdup("");
ans = new(char, MIN(strlen(s), console_width) * 4 + 1);
if (!ans)
return NULL;
for (f = s, t = r; *f; f++) {
memset(ans, '_', MIN(strlen(s), console_width) * 4);
ans[MIN(strlen(s), console_width) * 4] = 0;
for (f = s, t = prev = prev2 = ans; ; f++) {
char *tmp_t = t;
if (!*f) {
*t = 0;
return ans;
}
if ((unsigned char) *f < ' ' || (!eight_bits && (unsigned char) *f >= 127) ||
*f == '\\' || strchr(bad, *f)) {
if ((size_t) (t - ans) + 4 > console_width)
break;
if ((*f < ' ') || (*f >= 127) ||
(*f == '\\') || strchr(bad, *f)) {
*(t++) = '\\';
*(t++) = 'x';
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
} else
} else {
if ((size_t) (t - ans) + 1 > console_width)
break;
*(t++) = *f;
}
*t = 0;
/* We might need to go back two cycles to fit three dots, so remember two positions */
prev2 = prev;
prev = tmp_t;
}
return r;
/* We can just write where we want, since chars are one-byte */
size_t c = MIN(console_width, 3u); /* If the console is too narrow, write fewer dots */
size_t off;
if (console_width - c >= (size_t) (t - ans))
off = (size_t) (t - ans);
else if (console_width - c >= (size_t) (prev - ans))
off = (size_t) (prev - ans);
else if (console_width - c >= (size_t) (prev2 - ans))
off = (size_t) (prev2 - ans);
else
off = console_width - c;
assert(off <= (size_t) (t - ans));
memcpy(ans + off, "...", c);
ans[off + c] = '\0';
return ans;
}
char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) {
if (eight_bit)
return xescape_full(str, "", console_width, true);
else
return utf8_escape_non_printable_full(str, console_width);
}
char *octescape(const char *s, size_t len) {

View file

@ -46,8 +46,12 @@ int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **r
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit);
char *xescape(const char *s, const char *bad);
char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits);
static inline char *xescape(const char *s, const char *bad) {
return xescape_full(s, bad, SIZE_MAX, false);
}
char *octescape(const char *s, size_t len);
char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit);
char *shell_escape(const char *s, const char *bad);
char* shell_maybe_quote(const char *s, EscapeStyle style);

View file

@ -135,7 +135,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
for (;; (*p)++, c = **p) {
if (c == 0)
goto finish_force_terminate;
else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_QUOTES)) {
else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_UNQUOTE)) {
quote = c;
break;
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {

View file

@ -7,7 +7,7 @@ typedef enum ExtractFlags {
EXTRACT_RELAX = 1 << 0,
EXTRACT_CUNESCAPE = 1 << 1,
EXTRACT_CUNESCAPE_RELAX = 1 << 2,
EXTRACT_QUOTES = 1 << 3,
EXTRACT_UNQUOTE = 1 << 3,
EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 4,
EXTRACT_RETAIN_ESCAPE = 1 << 5,
} ExtractFlags;

View file

@ -21,6 +21,7 @@
#include "log.h"
#include "macro.h"
#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "stdio-util.h"
@ -174,6 +175,12 @@ int write_string_file_ts(
/* We don't know how to verify whether the file contents was already on-disk. */
assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC)));
if (flags & WRITE_STRING_FILE_MKDIR_0755) {
r = mkdir_parents(fn, 0755);
if (r < 0)
return r;
}
if (flags & WRITE_STRING_FILE_ATOMIC) {
assert(flags & WRITE_STRING_FILE_CREATE);
@ -582,10 +589,7 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
_cleanup_free_ char *p = NULL;
FILE *f;
if (root)
p = strjoin(root, *i, "/", path);
else
p = strjoin(*i, "/", path);
p = path_join(root, *i, path);
if (!p)
return -ENOMEM;

View file

@ -21,6 +21,7 @@ typedef enum {
WRITE_STRING_FILE_SYNC = 1 << 4,
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5,
WRITE_STRING_FILE_NOFOLLOW = 1 << 6,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 7,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()

View file

@ -0,0 +1,70 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <stdio.h>
#include "format-util.h"
#include "memory-util.h"
char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
/* Buffer is always cleared */
memzero(buf, IF_NAMESIZE + 1);
return if_indextoname(ifindex, buf);
}
char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
typedef struct {
const char *suffix;
uint64_t factor;
} suffix_table;
static const suffix_table table_iec[] = {
{ "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "M", UINT64_C(1024)*UINT64_C(1024) },
{ "K", UINT64_C(1024) },
}, table_si[] = {
{ "E", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
{ "P", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
{ "T", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
{ "G", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
{ "M", UINT64_C(1000)*UINT64_C(1000) },
{ "K", UINT64_C(1000) },
};
const suffix_table *table;
size_t n, i;
assert_cc(ELEMENTSOF(table_iec) == ELEMENTSOF(table_si));
if (t == (uint64_t) -1)
return NULL;
table = flag & FORMAT_BYTES_USE_IEC ? table_iec : table_si;
n = ELEMENTSOF(table_iec);
for (i = 0; i < n; i++)
if (t >= table[i].factor) {
if (flag & FORMAT_BYTES_BELOW_POINT) {
snprintf(buf, l,
"%" PRIu64 ".%" PRIu64 "%s",
t / table[i].factor,
i != n - 1 ?
(t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
(t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
table[i].suffix);
} else
snprintf(buf, l,
"%" PRIu64 "%s",
t / table[i].factor,
table[i].suffix);
goto finish;
}
snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
finish:
buf[l-1] = 0;
return buf;
}

View file

@ -0,0 +1,83 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <inttypes.h>
#include <net/if.h>
#include <stdbool.h>
#if SIZEOF_PID_T == 4
# define PID_PRI PRIi32
#elif SIZEOF_PID_T == 2
# define PID_PRI PRIi16
#else
# error Unknown pid_t size
#endif
#define PID_FMT "%" PID_PRI
#if SIZEOF_UID_T == 4
# define UID_FMT "%" PRIu32
#elif SIZEOF_UID_T == 2
# define UID_FMT "%" PRIu16
#else
# error Unknown uid_t size
#endif
#if SIZEOF_GID_T == 4
# define GID_FMT "%" PRIu32
#elif SIZEOF_GID_T == 2
# define GID_FMT "%" PRIu16
#else
# error Unknown gid_t size
#endif
#if SIZEOF_TIME_T == 8
# define PRI_TIME PRIi64
#elif SIZEOF_TIME_T == 4
# define PRI_TIME "li"
#else
# error Unknown time_t size
#endif
#if defined __x86_64__ && defined __ILP32__
# define PRI_TIMEX PRIi64
#else
# define PRI_TIMEX "li"
#endif
#if SIZEOF_RLIM_T == 8
# define RLIM_FMT "%" PRIu64
#elif SIZEOF_RLIM_T == 4
# define RLIM_FMT "%" PRIu32
#else
# error Unknown rlim_t size
#endif
#if SIZEOF_DEV_T == 8
# define DEV_FMT "%" PRIu64
#elif SIZEOF_DEV_T == 4
# define DEV_FMT "%" PRIu32
#else
# error Unknown dev_t size
#endif
#if SIZEOF_INO_T == 8
# define INO_FMT "%" PRIu64
#elif SIZEOF_INO_T == 4
# define INO_FMT "%" PRIu32
#else
# error Unknown ino_t size
#endif
char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]);
typedef enum {
FORMAT_BYTES_USE_IEC = 1 << 0,
FORMAT_BYTES_BELOW_POINT = 1 << 1,
FORMAT_BYTES_TRAILING_B = 1 << 2,
} FormatBytesFlag;
#define FORMAT_BYTES_MAX 8
char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag);
static inline char *format_bytes(char *buf, size_t l, uint64_t t) {
return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B);
}

View file

@ -213,113 +213,65 @@ int readlink_and_make_absolute(const char *p, char **r) {
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
_cleanup_close_ int fd = -1;
bool st_valid = false;
struct stat st;
int r;
assert(path);
/* Under the assumption that we are running privileged we first change the access mode and only then
* hand out ownership to avoid a window where access is too open. */
fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change
* mode/owner on the same file */
if (fd < 0)
return -errno;
xsprintf(fd_path, "/proc/self/fd/%i", fd);
if (mode != MODE_INVALID) {
if ((mode & S_IFMT) != 0) {
if (stat(fd_path, &st) < 0)
return -errno;
if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
return -EINVAL;
st_valid = true;
}
if (chmod(fd_path, mode & 07777) < 0) {
r = -errno;
if (!st_valid && stat(fd_path, &st) < 0)
return -errno;
if ((mode & 07777) != (st.st_mode & 07777))
return r;
st_valid = true;
}
}
if (uid != UID_INVALID || gid != GID_INVALID) {
if (chown(fd_path, uid, gid) < 0) {
r = -errno;
if (!st_valid && stat(fd_path, &st) < 0)
return -errno;
if (uid != UID_INVALID && st.st_uid != uid)
return r;
if (gid != GID_INVALID && st.st_gid != gid)
return r;
}
}
return 0;
return fchmod_and_chown(fd, mode, uid, gid);
}
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
bool st_valid = false;
bool do_chown, do_chmod;
struct stat st;
int r;
/* Under the assumption that we are running privileged we first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
if (mode != MODE_INVALID) {
if ((mode & S_IFMT) != 0) {
/* Change ownership and access mode of the specified fd. Tries to do so safely, ensuring that at no
* point in time the access mode is above the old access mode under the old ownership or the new
* access mode under the new ownership. Note: this call tries hard to leave the access mode
* unaffected if the uid/gid is changed, i.e. it undoes implicit suid/sgid dropping the kernel does
* on chown().
*
* This call is happy with O_PATH fds. */
if (fstat(fd, &st) < 0)
return -errno;
if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
return -EINVAL;
do_chown =
(uid != UID_INVALID && st.st_uid != uid) ||
(gid != GID_INVALID && st.st_gid != gid);
st_valid = true;
do_chmod =
!S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */
((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) ||
do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown()
* modifies the access mode too */
if (mode == MODE_INVALID)
mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */
else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0)
return -EINVAL; /* insist on the right file type if it was specified */
if (do_chown && do_chmod) {
mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */
if (((minimal ^ st.st_mode) & 07777) != 0)
if (fchmod_opath(fd, minimal & 07777) < 0)
return -errno;
}
if (fchmod(fd, mode & 07777) < 0) {
r = -errno;
if (!st_valid && fstat(fd, &st) < 0)
if (do_chown)
if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0)
return -errno;
if ((mode & 07777) != (st.st_mode & 07777))
return r;
st_valid = true;
}
}
if (uid != UID_INVALID || gid != GID_INVALID)
if (fchown(fd, uid, gid) < 0) {
r = -errno;
if (!st_valid && fstat(fd, &st) < 0)
if (do_chmod)
if (fchmod_opath(fd, mode & 07777) < 0)
return -errno;
if (uid != UID_INVALID && st.st_uid != uid)
return r;
if (gid != GID_INVALID && st.st_gid != gid)
return r;
}
return 0;
return do_chown || do_chmod;
}
int fchmod_umask(int fd, mode_t m) {
@ -404,13 +356,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
* 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;
ret = fchmod_and_chown(fd, mode, uid, gid);
if (stamp != USEC_INFINITY) {
struct timespec ts[2];
@ -1034,9 +980,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
/* Prefix what's left to do with what we just read, and start the loop again, but
* remain in the current directory. */
joined = strjoin(destination, todo);
joined = path_join(destination, todo);
} else
joined = strjoin("/", destination, todo);
joined = path_join("/", destination, todo);
if (!joined)
return -ENOMEM;

View file

@ -7,12 +7,20 @@
#include <stdbool.h>
#include <stdint.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "errno-util.h"
#include "time-util.h"
#define MODE_INVALID ((mode_t) -1)
/* The following macros add 1 when converting things, since 0 is a valid mode, while the pointer
* NULL is special */
#define PTR_TO_MODE(p) ((mode_t) ((uintptr_t) (p)-1))
#define MODE_TO_PTR(u) ((void *) ((uintptr_t) (u)+1))
int unlink_noerrno(const char *path);
int rmdir_parents(const char *path, const char *stop);

View file

@ -11,6 +11,7 @@
#include "macro.h"
#include "memory-util.h"
#include "mempool.h"
#include "missing.h"
#include "process-util.h"
#include "random-util.h"
#include "set.h"
@ -285,7 +286,11 @@ _destructor_ static void cleanup_pools(void) {
/* The pool is only allocated by the main thread, but the memory can
* be passed to other threads. Let's clean up if we are the main thread
* and no other threads are live. */
if (!is_main_thread())
/* We build our own is_main_thread() here, which doesn't use C11
* TLS based caching of the result. That's because valgrind apparently
* doesn't like malloc() (which C11 TLS internally uses) to be called
* from a GCC destructors. */
if (getpid() != gettid())
return;
r = get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t);

View file

@ -738,3 +738,15 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
}
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
static void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
assert(addr);
siphash24_compress(addr, sizeof(*addr), state);
}
static int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
return memcmp(a, b, sizeof(*a));
}
DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);

View file

@ -72,3 +72,4 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
extern const struct hash_ops in_addr_data_hash_ops;
extern const struct hash_ops in6_addr_hash_ops;

View file

@ -262,3 +262,85 @@ char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *f
iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
return x;
}
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
char *x;
x = set_iovec_string_field(iovec, n_iovec, field, value);
free(value);
return x;
}
struct iovec_wrapper *iovw_new(void) {
return malloc0(sizeof(struct iovec_wrapper));
}
void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
if (free_vectors)
for (size_t i = 0; i < iovw->count; i++)
free(iovw->iovec[i].iov_base);
iovw->iovec = mfree(iovw->iovec);
iovw->count = 0;
iovw->size_bytes = 0;
}
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
iovw_free_contents(iovw, true);
return mfree(iovw);
}
struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) {
iovw_free_contents(iovw, false);
return mfree(iovw);
}
int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
if (iovw->count >= IOV_MAX)
return -E2BIG;
if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
return log_oom();
iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
return 0;
}
int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value) {
_cleanup_free_ char *x = NULL;
int r;
x = strappend(field, value);
if (!x)
return log_oom();
r = iovw_put(iovw, x, strlen(x));
if (r >= 0)
TAKE_PTR(x);
return r;
}
int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value) {
_cleanup_free_ _unused_ char *free_ptr = value;
return iovw_put_string_field(iovw, field, value);
}
void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
size_t i;
for (i = 0; i < iovw->count; i++)
iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new;
}
size_t iovw_size(struct iovec_wrapper *iovw) {
size_t n = 0, i;
for (i = 0; i < iovw->count; i++)
n += iovw->iovec[i].iov_len;
return n;
}

View file

@ -73,3 +73,20 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string)
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
struct iovec_wrapper {
struct iovec *iovec;
size_t count;
size_t size_bytes;
};
struct iovec_wrapper *iovw_new(void);
struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
size_t iovw_size(struct iovec_wrapper *iovw);

View file

@ -37,8 +37,8 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
#define memzero(x,l) \
({ \
size_t _l_ = (l); \
void *_x_ = (x); \
_l_ == 0 ? _x_ : memset(_x_, 0, _l_); \
if (_l_ > 0) \
memset(x, 0, _l_); \
})
#define zero(x) (memzero(&(x), sizeof(x)))

View file

@ -4,6 +4,7 @@
#include <inttypes.h>
#include <linux/oom.h>
#include <locale.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -83,6 +84,9 @@ int parse_mode(const char *s, mode_t *ret) {
int parse_ifindex(const char *s, int *ret) {
int ifi, r;
assert(s);
assert(ret);
r = safe_atoi(s, &ifi);
if (r < 0)
return r;
@ -93,6 +97,24 @@ int parse_ifindex(const char *s, int *ret) {
return 0;
}
int parse_ifindex_or_ifname(const char *s, int *ret) {
int r;
assert(s);
assert(ret);
r = parse_ifindex(s, ret);
if (r >= 0)
return r;
r = (int) if_nametoindex(s);
if (r <= 0)
return -errno;
*ret = r;
return 0;
}
int parse_mtu(int family, const char *s, uint32_t *ret) {
uint64_t u;
size_t m;
@ -340,47 +362,6 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
return 0;
}
char *format_bytes(char *buf, size_t l, uint64_t t) {
unsigned i;
/* This only does IEC units so far */
static const struct {
const char *suffix;
uint64_t factor;
} table[] = {
{ "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "M", UINT64_C(1024)*UINT64_C(1024) },
{ "K", UINT64_C(1024) },
};
if (t == (uint64_t) -1)
return NULL;
for (i = 0; i < ELEMENTSOF(table); i++) {
if (t >= table[i].factor) {
snprintf(buf, l,
"%" PRIu64 ".%" PRIu64 "%s",
t / table[i].factor,
((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10),
table[i].suffix);
goto finish;
}
}
snprintf(buf, l, "%" PRIu64 "B", t);
finish:
buf[l-1] = 0;
return buf;
}
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
char *x = NULL;
unsigned long l;

View file

@ -9,13 +9,12 @@
#include "macro.h"
#define MODE_INVALID ((mode_t) -1)
int parse_boolean(const char *v) _pure_;
int parse_dev(const char *s, dev_t *ret);
int parse_pid(const char *s, pid_t* ret_pid);
int parse_mode(const char *s, mode_t *ret);
int parse_ifindex(const char *s, int *ret);
int parse_ifindex_or_ifname(const char *s, int *ret);
int parse_mtu(int family, const char *s, uint32_t *ret);
int parse_size(const char *t, uint64_t base, uint64_t *size);
@ -23,9 +22,6 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper);
int parse_errno(const char *t);
int parse_syscall_and_errno(const char *in, char **name, int *error);
#define FORMAT_BYTES_MAX 8
char *format_bytes(char *buf, size_t l, uint64_t t);
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
static inline int safe_atou(const char *s, unsigned *ret_u) {

View file

@ -68,10 +68,7 @@ char *path_make_absolute(const char *p, const char *prefix) {
if (path_is_absolute(p) || isempty(prefix))
return strdup(p);
if (endswith(prefix, "/"))
return strjoin(prefix, p);
else
return strjoin(prefix, "/", p);
return path_join(prefix, p);
}
int safe_getcwd(char **ret) {
@ -256,7 +253,7 @@ char **path_strv_resolve(char **l, const char *root) {
if (root) {
orig = *s;
t = prefix_root(root, orig);
t = path_join(root, orig);
if (!t) {
enomem = true;
continue;
@ -581,7 +578,7 @@ int find_binary(const char *name, char **ret) {
if (!path_is_absolute(element))
continue;
j = strjoin(element, "/", name);
j = path_join(element, name);
if (!j)
return -ENOMEM;
@ -686,40 +683,6 @@ int mkfs_exists(const char *fstype) {
return binary_is_good(mkfs);
}
char *prefix_root(const char *root, const char *path) {
char *n, *p;
size_t l;
/* If root is passed, prefixes path with it. Otherwise returns
* it as is. */
assert(path);
/* First, drop duplicate prefixing slashes from the path */
while (path[0] == '/' && path[1] == '/')
path++;
if (empty_or_root(root))
return strdup(path);
l = strlen(root) + 1 + strlen(path) + 1;
n = new(char, l);
if (!n)
return NULL;
p = stpcpy(n, root);
while (p > n && p[-1] == '/')
p--;
if (path[0] != '/')
*(p++) = '/';
strcpy(p, path);
return n;
}
int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
char *p;
int r;
@ -1027,7 +990,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
_cleanup_free_ char *path = NULL;
char *c, **name;
path = prefix_root(root, pattern);
path = path_join(root, pattern);
if (!path)
return -ENOMEM;

View file

@ -116,10 +116,8 @@ int mkfs_exists(const char *fstype);
_slash && ((*_slash = 0), true); \
_slash = strrchr((prefix), '/'))
char *prefix_root(const char *root, const char *path);
/* Similar to prefix_root(), but returns an alloca() buffer, or
* possibly a const pointer into the path parameter */
/* Similar to path_join(), but only works for two components, and only the first one may be NULL and returns
* an alloca() buffer, or possibly a const pointer into the path parameter. */
#define prefix_roota(root, path) \
({ \
const char* _path = (path), *_root = (root), *_ret; \
@ -127,7 +125,7 @@ char *prefix_root(const char *root, const char *path);
size_t _l; \
while (_path[0] == '/' && _path[1] == '/') \
_path ++; \
if (empty_or_root(_root)) \
if (isempty(_root)) \
_ret = _path; \
else { \
_l = strlen(_root) + 1 + strlen(_path) + 1; \

View file

@ -4,7 +4,6 @@
#include <errno.h>
#include <limits.h>
#include <linux/oom.h>
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
@ -25,10 +24,12 @@
#include "alloc-util.h"
#include "architecture.h"
#include "escape.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "ioprio.h"
#include "locale-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
@ -43,8 +44,14 @@
#include "string-util.h"
#include "terminal-util.h"
#include "user-util.h"
#include "utf8.h"
int get_process_state(pid_t pid) {
/* The kernel limits userspace processes to TASK_COMM_LEN (16 bytes), but allows higher values for its own
* workers, e.g. "kworker/u9:3-kcryptd/253:0". Let's pick a fixed smallish limit that will work for the kernel.
*/
#define COMM_MAX_LEN 128
static int get_process_state(pid_t pid) {
const char *p;
char state;
int r;
@ -80,7 +87,7 @@ int get_process_comm(pid_t pid, char **ret) {
assert(ret);
assert(pid >= 0);
escaped = new(char, TASK_COMM_LEN);
escaped = new(char, COMM_MAX_LEN);
if (!escaped)
return -ENOMEM;
@ -93,28 +100,31 @@ int get_process_comm(pid_t pid, char **ret) {
return r;
/* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */
cellescape(escaped, TASK_COMM_LEN, comm);
cellescape(escaped, COMM_MAX_LEN, comm);
*ret = TAKE_PTR(escaped);
return 0;
}
int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
_cleanup_fclose_ FILE *f = NULL;
bool space = false;
char *k;
_cleanup_free_ char *ans = NULL;
_cleanup_free_ char *t = NULL, *ans = NULL;
const char *p;
int c, r;
int r;
size_t k;
/* This is supposed to be a safety guard against runaway command lines. */
size_t max_length = sc_arg_max();
assert(line);
assert(pid >= 0);
/* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing
* multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most
* (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If
* comm_fallback is true and the process has no command line set (the case for kernel threads), or has a
* command line that resolves to the empty string will return the "comm" name of the process instead.
/* Retrieves a process' command line. Replaces non-utf8 bytes by replacement character (<28>). If
* max_columns is != -1 will return a string of the specified console width at most, abbreviated with
* an ellipsis. If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command
* line set (the case for kernel threads), or has a command line that resolves to the empty string
* will return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of
* input data.
*
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
* comm_fallback is false). Returns 0 and sets *line otherwise. */
@ -126,130 +136,56 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
if (r < 0)
return r;
if (max_length == 0) {
/* This is supposed to be a safety guard against runaway command lines. */
long l = sysconf(_SC_ARG_MAX);
assert(l > 0);
max_length = l;
}
/* We assume that each four-byte character uses one or two columns. If we ever check for combining
* characters, this assumption will need to be adjusted. */
if ((size_t) 4 * max_columns + 1 < max_columns)
max_length = MIN(max_length, (size_t) 4 * max_columns + 1);
if (max_length == 1) {
/* If there's only room for one byte, return the empty string */
ans = new0(char, 1);
if (!ans)
t = new(char, max_length);
if (!t)
return -ENOMEM;
*line = TAKE_PTR(ans);
return 0;
k = fread(t, 1, max_length, f);
if (k > 0) {
/* Arguments are separated by NULs. Let's replace those with spaces. */
for (size_t i = 0; i < k - 1; i++)
if (t[i] == '\0')
t[i] = ' ';
t[k] = '\0'; /* Normally, t[k] is already NUL, so this is just a guard in case of short read */
} else {
bool dotdotdot = false;
size_t left;
/* We only treat getting nothing as an error. We *could* also get an error after reading some
* data, but we ignore that case, as such an error is rather unlikely and we prefer to get
* some data rather than none. */
if (ferror(f))
return -errno;
ans = new(char, max_length);
if (!ans)
return -ENOMEM;
k = ans;
left = max_length;
while ((c = getc(f)) != EOF) {
if (isprint(c)) {
if (space) {
if (left <= 2) {
dotdotdot = true;
break;
}
*(k++) = ' ';
left--;
space = false;
}
if (left <= 1) {
dotdotdot = true;
break;
}
*(k++) = (char) c;
left--;
} else if (k > ans)
space = true;
}
if (dotdotdot) {
if (max_length <= 4) {
k = ans;
left = max_length;
} else {
k = ans + max_length - 4;
left = 4;
/* Eat up final spaces */
while (k > ans && isspace(k[-1])) {
k--;
left++;
}
}
strncpy(k, "...", left-1);
k[left-1] = 0;
} else
*k = 0;
}
/* Kernel threads have no argv[] */
if (isempty(ans)) {
_cleanup_free_ char *t = NULL;
int h;
ans = mfree(ans);
if (!comm_fallback)
if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
return -ENOENT;
h = get_process_comm(pid, &t);
if (h < 0)
return h;
/* Kernel threads have no argv[] */
_cleanup_free_ char *t2 = NULL;
size_t l = strlen(t);
r = get_process_comm(pid, &t2);
if (r < 0)
return r;
if (l + 3 <= max_length) {
ans = strjoin("[", t, "]");
if (!ans)
mfree(t);
t = strjoin("[", t2, "]");
if (!t)
return -ENOMEM;
}
} else if (max_length <= 6) {
ans = new(char, max_length);
if (!ans)
return -ENOMEM;
memcpy(ans, "[...]", max_length-1);
ans[max_length-1] = 0;
} else {
t[max_length - 6] = 0;
/* Chop off final spaces */
delete_trailing_chars(t, WHITESPACE);
ans = strjoin("[", t, "...]");
bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8();
ans = escape_non_printable_full(t, max_columns, eight_bit);
if (!ans)
return -ENOMEM;
}
(void) str_realloc(&ans);
*line = TAKE_PTR(ans);
return 0;
}
k = realloc(ans, strlen(ans) + 1);
if (!k)
return -ENOMEM;
ans = NULL;
*line = k;
return 0;
}
@ -281,7 +217,7 @@ int rename_process(const char name[]) {
* can use PR_SET_NAME, which sets the thread name for the calling thread. */
if (prctl(PR_SET_NAME, name) < 0)
log_debug_errno(errno, "PR_SET_NAME failed: %m");
if (l >= TASK_COMM_LEN) /* Linux process names can be 15 chars at max */
if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
truncated = true;
/* Second step, change glibc's ID of the process name. */
@ -1534,45 +1470,11 @@ int set_oom_score_adjust(int value) {
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
}
int cpus_in_affinity_mask(void) {
size_t n = 16;
int r;
for (;;) {
cpu_set_t *c;
c = CPU_ALLOC(n);
if (!c)
return -ENOMEM;
if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
int k;
k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c);
CPU_FREE(c);
if (k <= 0)
return -EINVAL;
return k;
}
r = -errno;
CPU_FREE(c);
if (r != -EINVAL)
return r;
if (n > SIZE_MAX/2)
return -ENOMEM;
n *= 2;
}
}
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
[IOPRIO_CLASS_BE] = "best-effort",
[IOPRIO_CLASS_IDLE] = "idle"
[IOPRIO_CLASS_IDLE] = "idle",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES);
@ -1593,7 +1495,7 @@ static const char* const sched_policy_table[] = {
[SCHED_BATCH] = "batch",
[SCHED_IDLE] = "idle",
[SCHED_FIFO] = "fifo",
[SCHED_RR] = "rr"
[SCHED_RR] = "rr",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);

View file

@ -31,9 +31,13 @@
_r_; \
})
int get_process_state(pid_t pid);
typedef enum ProcessCmdlineFlags {
PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
PROCESS_CMDLINE_USE_LOCALE = 1 << 1,
} ProcessCmdlineFlags;
int get_process_comm(pid_t pid, char **name);
int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line);
int get_process_exe(pid_t pid, char **name);
int get_process_uid(pid_t pid, uid_t *uid);
int get_process_gid(pid_t pid, gid_t *gid);
@ -194,5 +198,3 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX)
(pid) = 0; \
_pid_; \
})
int cpus_in_affinity_mask(void);

View file

@ -33,6 +33,66 @@
int rdrand(unsigned long *ret) {
/* So, you are a "security researcher", and you wonder why we bother with using raw RDRAND here,
* instead of sticking to /dev/urandom or getrandom()?
*
* Here's why: early boot. On Linux, during early boot the random pool that backs /dev/urandom and
* getrandom() is generally not initialized yet. It is very common that initialization of the random
* pool takes a longer time (up to many minutes), in particular on embedded devices that have no
* explicit hardware random generator, as well as in virtualized environments such as major cloud
* installations that do not provide virtio-rng or a similar mechanism.
*
* In such an environment using getrandom() synchronously means we'd block the entire system boot-up
* until the pool is initialized, i.e. *very* long. Using getrandom() asynchronously (GRND_NONBLOCK)
* would mean acquiring randomness during early boot would simply fail. Using /dev/urandom would mean
* generating many kmsg log messages about our use of it before the random pool is properly
* initialized. Neither of these outcomes is desirable.
*
* Thus, for very specific purposes we use RDRAND instead of either of these three options. RDRAND
* provides us quickly and relatively reliably with random values, without having to delay boot,
* without triggering warning messages in kmsg.
*
* Note that we use RDRAND only under very specific circumstances, when the requirements on the
* quality of the returned entropy permit it. Specifically, here are some cases where we *do* use
* RDRAND:
*
* UUID generation: UUIDs are supposed to be universally unique but are not cryptographic
* key material. The quality and trust level of RDRAND should hence be OK: UUIDs should be
* generated in a way that is reliably unique, but they do not require ultimate trust into
* the entropy generator. systemd generates a number of UUIDs during early boot, including
* 'invocation IDs' for every unit spawned that identify the specific invocation of the
* service globally, and a number of others. Other alternatives for generating these UUIDs
* have been considered, but don't really work: for example, hashing uuids from a local
* system identifier combined with a counter falls flat because during early boot disk
* storage is not yet available (think: initrd) and thus a system-specific ID cannot be
* stored or retrieved yet.
*
* Hash table seed generation: systemd uses many hash tables internally. Hash tables are
* generally assumed to have O(1) access complexity, but can deteriorate to prohibitive
* O(n) access complexity if an attacker manages to trigger a large number of hash
* collisions. Thus, systemd (as any software employing hash tables should) uses seeded
* hash functions for its hash tables, with a seed generated randomly. The hash tables
* systemd employs watch the fill level closely and reseed if necessary. This allows use of
* a low quality RNG initially, as long as it improves should a hash table be under attack:
* the attacker after all needs to to trigger many collisions to exploit it for the purpose
* of DoS, but if doing so improves the seed the attack surface is reduced as the attack
* takes place.
*
* Some cases where we do NOT use RDRAND are:
*
* Generation of cryptographic key material 🔑
*
* Generation of cryptographic salt values 🧂
*
* This function returns:
*
* -EOPNOTSUPP RDRAND is not available on this system 😔
* -EAGAIN The operation failed this time, but is likely to work if you try again a few
* times
* -EUCLEAN We got some random value, but it looked strange, so we refused using it.
* This failure might or might not be temporary. 😕
*/
#if defined(__i386__) || defined(__x86_64__)
static int have_rdrand = -1;
unsigned long v;
@ -90,10 +150,20 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
bool got_some = false;
int r;
/* Gathers some randomness from the kernel (or the CPU if the RANDOM_ALLOW_RDRAND flag is set). This
* call won't block, unless the RANDOM_BLOCK flag is set. If RANDOM_MAY_FAIL is set, an error is
* returned if the random pool is not initialized. Otherwise it will always return some data from the
* kernel, regardless of whether the random pool is fully initialized or not. */
/* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from
* the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK
* flag is set. If RANDOM_MAY_FAIL is set, an error is returned if the random pool is not
* initialized. Otherwise it will always return some data from the kernel, regardless of whether the
* random pool is fully initialized or not. If RANDOM_EXTEND_WITH_PSEUDO is set, and some but not
* enough better quality randomness could be acquired, the rest is filled up with low quality
* randomness.
*
* Of course, when creating cryptographic key material you really shouldn't use RANDOM_ALLOW_DRDRAND
* or even RANDOM_EXTEND_WITH_PSEUDO.
*
* When generating UUIDs it's fine to use RANDOM_ALLOW_RDRAND but not OK to use
* RANDOM_EXTEND_WITH_PSEUDO. In fact RANDOM_EXTEND_WITH_PSEUDO is only really fine when invoked via
* an "all bets are off" wrapper, such as random_bytes(), see below. */
if (n == 0)
return 0;
@ -255,6 +325,11 @@ void initialize_srand(void) {
void pseudo_random_bytes(void *p, size_t n) {
uint8_t *q;
/* This returns pseudo-random data using libc's rand() function. You probably never want to call this
* directly, because why would you use this if you can get better stuff cheaply? Use random_bytes()
* instead, see below: it will fall back to this function if there's nothing better to get, but only
* then. */
initialize_srand();
for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) {
@ -276,6 +351,38 @@ void pseudo_random_bytes(void *p, size_t n) {
void random_bytes(void *p, size_t n) {
/* This returns high quality randomness if we can get it cheaply. If we can't because for some reason
* it is not available we'll try some crappy fallbacks.
*
* What this function will do:
*
* This function will preferably use the CPU's RDRAND operation, if it is available, in
* order to return "mid-quality" random values cheaply.
*
* Use getrandom() with GRND_NONBLOCK, to return high-quality random values if they are
* cheaply available.
*
* This function will return pseudo-random data, generated via libc rand() if nothing
* better is available.
*
* This function will work fine in early boot
*
* This function will always succeed
*
* What this function won't do:
*
* This function will never fail: it will give you randomness no matter what. It might not
* be high quality, but it will return some, possibly generated via libc's rand() call.
*
* This function will never block: if the only way to get good randomness is a blocking,
* synchronous getrandom() we'll instead provide you with pseudo-random data.
*
* This function is hence great for things like seeding hash tables, generating random numeric UNIX
* user IDs (that are checked for collisions before use) and such.
*
* This function is hence not useful for generating UUIDs or cryptographic key material.
*/
if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND) >= 0)
return;

View file

@ -17,6 +17,7 @@ struct siphash {
void siphash24_init(struct siphash *state, const uint8_t k[static 16]);
void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
void siphash24_compress_boolean(bool in, struct siphash *state);
#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
uint64_t siphash24_finalize(struct siphash *state);

View file

@ -1389,7 +1389,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
}
int socket_bind_to_ifindex(int fd, int ifindex) {
char ifname[IFNAMSIZ] = "";
char ifname[IF_NAMESIZE + 1];
assert(fd >= 0);
@ -1407,7 +1407,7 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
return -errno;
/* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
if (!if_indextoname(ifindex, ifname))
if (!format_ifname(ifindex, ifname))
return -errno;
return socket_bind_to_ifname(fd, ifname);

View file

@ -9,6 +9,7 @@
#include <netinet/in.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>

View file

@ -257,3 +257,16 @@ static inline void *memory_startswith_no_case(const void *p, size_t sz, const ch
return (uint8_t*) p + n;
}
static inline char* str_realloc(char **p) {
/* Reallocate *p to actual size */
if (!*p)
return NULL;
char *t = realloc(*p, strlen(*p) + 1);
if (!t)
return NULL;
return (*p = t);
}

View file

@ -575,7 +575,6 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
*/
assert(t);
assert(usec);
if (t[0] == '@' && !with_tz)
return parse_sec(t + 1, usec);
@ -803,8 +802,8 @@ finish:
else
return -EINVAL;
if (usec)
*usec = ret;
return 0;
}
@ -861,7 +860,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
if (munmap(shared, sizeof *shared) != 0)
return negative_errno();
if (tmp.return_value == 0)
if (tmp.return_value == 0 && usec)
*usec = tmp.usec;
return tmp.return_value;
@ -923,7 +922,6 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
bool something = false;
assert(t);
assert(usec);
assert(default_unit > 0);
p = t;
@ -935,6 +933,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
if (*s != 0)
return -EINVAL;
if (usec)
*usec = USEC_INFINITY;
return 0;
}
@ -1007,8 +1006,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
}
}
if (usec)
*usec = r;
return 0;
}

View file

@ -58,13 +58,11 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
/* This is much like mkostemp() but is subject to umask(). */
int mkostemp_safe(char *pattern) {
_cleanup_umask_ mode_t u = 0;
_unused_ _cleanup_umask_ mode_t u = umask(0077);
int fd;
assert(pattern);
u = umask(077);
fd = mkostemp(pattern, O_CLOEXEC);
if (fd < 0)
return -errno;
@ -322,7 +320,7 @@ int mkdtemp_malloc(const char *template, char **ret) {
if (r < 0)
return r;
p = strjoin(tmp, "/XXXXXX");
p = path_join(tmp, "XXXXXX");
}
if (!p)
return -ENOMEM;

View file

@ -32,6 +32,7 @@
#include "gunicode.h"
#include "hexdecoct.h"
#include "macro.h"
#include "string-util.h"
#include "utf8.h"
bool unichar_is_valid(char32_t ch) {
@ -192,46 +193,93 @@ char *utf8_escape_invalid(const char *str) {
}
*s = '\0';
(void) str_realloc(&p);
return p;
}
char *utf8_escape_non_printable(const char *str) {
char *p, *s;
static int utf8_char_console_width(const char *str) {
char32_t c;
int r;
r = utf8_encoded_to_unichar(str, &c);
if (r < 0)
return r;
/* TODO: we should detect combining characters */
return unichar_iswide(c) ? 2 : 1;
}
char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
char *p, *s, *prev_s;
size_t n = 0; /* estimated print width */
assert(str);
p = s = malloc(strlen(str) * 4 + 1);
if (console_width == 0)
return strdup("");
p = s = prev_s = malloc(strlen(str) * 4 + 1);
if (!p)
return NULL;
while (*str) {
for (;;) {
int len;
char *saved_s = s;
if (!*str) /* done! */
goto finish;
len = utf8_encoded_valid_unichar(str, (size_t) -1);
if (len > 0) {
if (utf8_is_printable(str, len)) {
int w;
w = utf8_char_console_width(str);
assert(w >= 0);
if (n + w > console_width)
goto truncation;
s = mempcpy(s, str, len);
str += len;
n += w;
} else {
while (len > 0) {
for (; len > 0; len--) {
if (n + 4 > console_width)
goto truncation;
*(s++) = '\\';
*(s++) = 'x';
*(s++) = hexchar((int) *str >> 4);
*(s++) = hexchar((int) *str);
str += 1;
len--;
n += 4;
}
}
} else {
s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
if (n + 1 > console_width)
goto truncation;
s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
str += 1;
}
n += 1;
}
prev_s = saved_s;
}
truncation:
/* Try to go back one if we don't have enough space for the ellipsis */
if (n + 1 >= console_width)
s = prev_s;
s = mempcpy(s, "", strlen(""));
finish:
*s = '\0';
(void) str_realloc(&p);
return p;
}
@ -519,15 +567,15 @@ size_t utf8_console_width(const char *str) {
/* Returns the approximate width a string will take on screen when printed on a character cell
* terminal/console. */
while (*str != 0) {
char32_t c;
while (*str) {
int w;
if (utf8_encoded_to_unichar(str, &c) < 0)
w = utf8_char_console_width(str);
if (w < 0)
return (size_t) -1;
n += w;
str = utf8_next_char(str);
n += unichar_iswide(c) ? 2 : 1;
}
return n;

View file

@ -22,7 +22,10 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pu
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
char *utf8_escape_invalid(const char *s);
char *utf8_escape_non_printable(const char *str);
char *utf8_escape_non_printable_full(const char *str, size_t console_width);
static inline char *utf8_escape_non_printable(const char *str) {
return utf8_escape_non_printable_full(str, (size_t) -1);
}
size_t utf8_encode_unichar(char *out_utf8, char32_t g);
size_t utf16_encode_unichar(char16_t *out, char32_t c);

View file

@ -190,7 +190,7 @@ int dhcp_identifier_set_iaid(
/* device is under renaming */
return -EBUSY;
name = net_get_name(device);
name = net_get_name_persistent(device);
}
}

View file

@ -198,7 +198,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
if (memchr(option, 0, len - 1))
return -EINVAL;
string = strndup((const char *) option, len);
string = memdup_suffix0((const char *) option, len);
if (!string)
return -ENOMEM;

View file

@ -12,6 +12,7 @@
#include "conf-parser.h"
#include "device-util.h"
#include "dhcp-lease-internal.h"
#include "env-util.h"
#include "ether-addr-util.h"
#include "hexdecoct.h"
#include "log.h"
@ -24,7 +25,7 @@
#include "utf8.h"
#include "util.h"
const char *net_get_name(sd_device *device) {
const char *net_get_name_persistent(sd_device *device) {
const char *name, *field;
assert(device);
@ -39,7 +40,7 @@ const char *net_get_name(sd_device *device) {
#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result) {
size_t l, sz = 0;
const char *name;
int r;
@ -47,10 +48,10 @@ int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
assert(device);
/* net_get_name() will return one of the device names based on stable information about the
* device. If this is not available, we fall back to using the device name. */
name = net_get_name(device);
if (!name)
/* net_get_name_persistent() will return one of the device names based on stable information about
* the device. If this is not available, we fall back to using the actual device name. */
name = net_get_name_persistent(device);
if (!name && use_sysname)
(void) sd_device_get_sysname(device, &name);
if (!name)
return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
@ -73,26 +74,66 @@ int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
return 0;
}
static bool net_condition_test_strv(char * const *raw_patterns,
const char *string) {
if (strv_isempty(raw_patterns))
static bool net_condition_test_strv(char * const *patterns, const char *string) {
char * const *p;
bool match = false, has_positive_rule = false;
if (strv_isempty(patterns))
return true;
/* If the patterns begin with "!", edit it out and negate the test. */
if (raw_patterns[0][0] == '!') {
char **patterns;
size_t i, length;
STRV_FOREACH(p, patterns) {
const char *q = *p;
bool invert;
length = strv_length(raw_patterns) + 1; /* Include the NULL. */
patterns = newa(char*, length);
patterns[0] = raw_patterns[0] + 1; /* Skip the "!". */
for (i = 1; i < length; i++)
patterns[i] = raw_patterns[i];
invert = *q == '!';
q += invert;
return !string || !strv_fnmatch(patterns, string, 0);
if (!invert)
has_positive_rule = true;
if (string && fnmatch(q, string, 0) == 0) {
if (invert)
return false;
else
match = true;
}
}
return string && strv_fnmatch(raw_patterns, string, 0);
return has_positive_rule ? match : true;
}
static int net_condition_test_property(char * const *match_property, sd_device *device) {
char * const *p;
if (strv_isempty(match_property))
return true;
STRV_FOREACH(p, match_property) {
_cleanup_free_ char *key = NULL;
const char *val, *dev_val;
bool invert, v;
invert = **p == '!';
val = strchr(*p + invert, '=');
if (!val)
return -EINVAL;
key = strndup(*p + invert, val - *p - invert);
if (!key)
return -ENOMEM;
val++;
v = device &&
sd_device_get_property_value(device, key, &dev_val) >= 0 &&
fnmatch(val, dev_val, 0) == 0;
if (invert ? v : !v)
return false;
}
return true;
}
bool net_match_config(Set *match_mac,
@ -100,12 +141,25 @@ bool net_match_config(Set *match_mac,
char * const *match_drivers,
char * const *match_types,
char * const *match_names,
char * const *match_property,
sd_device *device,
const struct ether_addr *dev_mac,
const char *dev_path,
const char *dev_driver,
const char *dev_type,
const char *dev_name) {
const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str;
if (device) {
(void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
(void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
(void) sd_device_get_devtype(device, &dev_type);
if (!dev_name)
(void) sd_device_get_sysname(device, &dev_name);
if (!dev_mac &&
sd_device_get_sysattr_value(device, "address", &mac_str) >= 0)
dev_mac = ether_aton(mac_str);
}
if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac)))
return false;
@ -121,6 +175,9 @@ bool net_match_config(Set *match_mac,
if (!net_condition_test_strv(match_names, dev_name))
return false;
if (!net_condition_test_property(match_property, device))
return false;
return true;
}
@ -164,7 +221,7 @@ int config_parse_net_condition(const char *unit,
return 0;
}
int config_parse_ifnames(
int config_parse_match_strv(
const char *unit,
const char *filename,
unsigned line,
@ -176,7 +233,9 @@ int config_parse_ifnames(
void *data,
void *userdata) {
const char *p = rvalue;
char ***sv = data;
bool invert;
int r;
assert(filename);
@ -184,30 +243,154 @@ int config_parse_ifnames(
assert(rvalue);
assert(data);
if (isempty(rvalue)) {
*sv = strv_free(*sv);
return 0;
}
invert = *p == '!';
p += invert;
for (;;) {
_cleanup_free_ char *word = NULL;
_cleanup_free_ char *word = NULL, *k = NULL;
r = extract_first_word(&rvalue, &word, NULL, 0);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue);
return 0;
}
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r == 0)
break;
if (!ifname_valid(word)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
return 0;
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
return 0;
}
r = strv_push(sv, word);
if (invert) {
k = strjoin("!", word);
if (!k)
return log_oom();
} else
k = TAKE_PTR(word);
r = strv_consume(sv, TAKE_PTR(k));
if (r < 0)
return log_oom();
}
}
word = NULL;
int config_parse_match_ifnames(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
const char *p = rvalue;
char ***sv = data;
bool invert;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
invert = *p == '!';
p += invert;
for (;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r == 0)
return 0;
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Failed to parse interface name list: %s", rvalue);
return 0;
}
if (!ifname_valid(word)) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Interface name is not valid or too long, ignoring assignment: %s", word);
continue;
}
if (invert) {
k = strjoin("!", word);
if (!k)
return log_oom();
} else
k = TAKE_PTR(word);
r = strv_consume(sv, TAKE_PTR(k));
if (r < 0)
return log_oom();
}
}
int config_parse_match_property(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
const char *p = rvalue;
char ***sv = data;
bool invert;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
invert = *p == '!';
p += invert;
for (;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
if (r == 0)
return 0;
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid syntax, ignoring: %s", rvalue);
return 0;
}
if (!env_assignment_is_valid(word)) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid property or value, ignoring assignment: %s", word);
continue;
}
if (invert) {
k = strjoin("!", word);
if (!k)
return log_oom();
} else
k = TAKE_PTR(word);
r = strv_consume(sv, TAKE_PTR(k));
if (r < 0)
return log_oom();
}
}
int config_parse_ifalias(const char *unit,

View file

@ -19,21 +19,22 @@ bool net_match_config(Set *match_mac,
char * const *match_driver,
char * const *match_type,
char * const *match_name,
char * const *match_property,
sd_device *device,
const struct ether_addr *dev_mac,
const char *dev_path,
const char *dev_driver,
const char *dev_type,
const char *dev_name);
CONFIG_PARSER_PROTOTYPE(config_parse_net_condition);
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs);
CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
CONFIG_PARSER_PROTOTYPE(config_parse_match_strv);
CONFIG_PARSER_PROTOTYPE(config_parse_match_ifnames);
CONFIG_PARSER_PROTOTYPE(config_parse_match_property);
CONFIG_PARSER_PROTOTYPE(config_parse_ifalias);
CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority);
int net_get_unique_predictable_data(sd_device *device, uint64_t *result);
const char *net_get_name(sd_device *device);
int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result);
const char *net_get_name_persistent(sd_device *device);
size_t serialize_in_addrs(FILE *f,
const struct in_addr *addresses,

View file

@ -1385,6 +1385,23 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
return 0;
}
static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
if (a->address != b->address)
return false;
if (a->subnet_mask != b->subnet_mask)
return false;
if (a->router_size != b->router_size)
return false;
for (size_t i = 0; i < a->router_size; i++)
if (a->router[i].s_addr != b->router[i].s_addr)
return false;
return true;
}
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) {
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
_cleanup_free_ char *error_message = NULL;
@ -1437,12 +1454,10 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le
r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
if (client->lease) {
if (client->lease->address != lease->address ||
client->lease->subnet_mask != lease->subnet_mask ||
client->lease->router != lease->router) {
r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
} else
if (lease_equal(client->lease, lease))
r = SD_DHCP_CLIENT_EVENT_RENEW;
else
r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
client->lease = sd_dhcp_lease_unref(client->lease);
}

View file

@ -333,7 +333,7 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
if (memchr(option, 0, len - 1))
return -EINVAL;
string = strndup((const char *) option, len);
string = memdup_suffix0((const char *) option, len);
if (!string)
return -ENOMEM;