mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-07-24 03:34:40 +00:00
systemd: merge branch systemd into master
This commit is contained in:
commit
9e9320cc0f
|
@ -1541,9 +1541,14 @@ src_libsystemd_nm_la_SOURCES = \
|
|||
src/systemd/sd-adapt/locale-util.h \
|
||||
src/systemd/sd-adapt/memfd-util.h \
|
||||
src/systemd/sd-adapt/missing.h \
|
||||
src/systemd/sd-adapt/missing_socket.h \
|
||||
src/systemd/sd-adapt/missing_syscall.h \
|
||||
src/systemd/sd-adapt/missing_timerfd.h \
|
||||
src/systemd/sd-adapt/missing_type.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/rlimit-util.h \
|
||||
src/systemd/sd-adapt/sd-daemon.h \
|
||||
src/systemd/sd-adapt/sd-device.h \
|
||||
src/systemd/sd-adapt/serialize.h \
|
||||
|
@ -1555,6 +1560,8 @@ src_libsystemd_nm_la_SOURCES = \
|
|||
src/systemd/src/basic/alloc-util.c \
|
||||
src/systemd/src/basic/alloc-util.h \
|
||||
src/systemd/src/basic/async.h \
|
||||
src/systemd/src/basic/env-file.c \
|
||||
src/systemd/src/basic/env-file.h \
|
||||
src/systemd/src/basic/env-util.c \
|
||||
src/systemd/src/basic/env-util.h \
|
||||
src/systemd/src/basic/escape.c \
|
||||
|
@ -1614,6 +1621,8 @@ src_libsystemd_nm_la_SOURCES = \
|
|||
src/systemd/src/basic/strv.h \
|
||||
src/systemd/src/basic/time-util.c \
|
||||
src/systemd/src/basic/time-util.h \
|
||||
src/systemd/src/basic/tmpfile-util.c \
|
||||
src/systemd/src/basic/tmpfile-util.h \
|
||||
src/systemd/src/basic/umask-util.h \
|
||||
src/systemd/src/basic/utf8.c \
|
||||
src/systemd/src/basic/utf8.h \
|
||||
|
|
|
@ -2,6 +2,7 @@ sources = files(
|
|||
'sd-adapt/nm-sd-adapt.c',
|
||||
'src/basic/alloc-util.c',
|
||||
'src/basic/escape.c',
|
||||
'src/basic/env-file.c',
|
||||
'src/basic/env-util.c',
|
||||
'src/basic/ether-addr-util.c',
|
||||
'src/basic/extract-word.c',
|
||||
|
@ -26,6 +27,7 @@ sources = files(
|
|||
'src/basic/string-util.c',
|
||||
'src/basic/strv.c',
|
||||
'src/basic/time-util.c',
|
||||
'src/basic/tmpfile-util.c',
|
||||
'src/basic/utf8.c',
|
||||
'src/basic/util.c',
|
||||
'src/libsystemd-network/arp-util.c',
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
/* dummy header */
|
||||
|
||||
#include "path-util.h"
|
||||
|
|
3
src/systemd/sd-adapt/missing_socket.h
Normal file
3
src/systemd/sd-adapt/missing_socket.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
/* dummy header */
|
3
src/systemd/sd-adapt/missing_syscall.h
Normal file
3
src/systemd/sd-adapt/missing_syscall.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
/* dummy header */
|
3
src/systemd/sd-adapt/missing_timerfd.h
Normal file
3
src/systemd/sd-adapt/missing_timerfd.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
/* dummy header */
|
3
src/systemd/sd-adapt/missing_type.h
Normal file
3
src/systemd/sd-adapt/missing_type.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
/* dummy header */
|
3
src/systemd/sd-adapt/rlimit-util.h
Normal file
3
src/systemd/sd-adapt/rlimit-util.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
/* dummy header */
|
|
@ -14,7 +14,7 @@ void* memdup(const void *p, size_t l) {
|
|||
|
||||
assert(l == 0 || p);
|
||||
|
||||
ret = malloc(l);
|
||||
ret = malloc(l ?: 1);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
|
||||
#include "macro.h"
|
||||
|
||||
typedef void (*free_func_t)(void *p);
|
||||
|
||||
#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
|
||||
|
||||
#define new0(t, n) ((t*) calloc((n), sizeof(t)))
|
||||
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
|
||||
|
||||
#define newa(t, n) \
|
||||
({ \
|
||||
|
@ -75,7 +77,7 @@ _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t
|
|||
if (size_multiply_overflow(size, need))
|
||||
return NULL;
|
||||
|
||||
return malloc(size * need);
|
||||
return malloc(size * need ?: 1);
|
||||
}
|
||||
|
||||
#if !HAVE_REALLOCARRAY
|
||||
|
@ -83,7 +85,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size
|
|||
if (size_multiply_overflow(size, need))
|
||||
return NULL;
|
||||
|
||||
return realloc(p, size * need);
|
||||
return realloc(p, size * need ?: 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
578
src/systemd/src/basic/env-file.c
Normal file
578
src/systemd/src/basic/env-file.c
Normal file
|
@ -0,0 +1,578 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include "nm-sd-adapt.h"
|
||||
|
||||
#include <stdio_ext.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "env-file.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "utf8.h"
|
||||
|
||||
static int parse_env_file_internal(
|
||||
FILE *f,
|
||||
const char *fname,
|
||||
int (*push) (const char *filename, unsigned line,
|
||||
const char *key, char *value, void *userdata, int *n_pushed),
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
|
||||
size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
|
||||
_cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL;
|
||||
unsigned line = 1;
|
||||
char *p;
|
||||
int r;
|
||||
|
||||
enum {
|
||||
PRE_KEY,
|
||||
KEY,
|
||||
PRE_VALUE,
|
||||
VALUE,
|
||||
VALUE_ESCAPE,
|
||||
SINGLE_QUOTE_VALUE,
|
||||
SINGLE_QUOTE_VALUE_ESCAPE,
|
||||
DOUBLE_QUOTE_VALUE,
|
||||
DOUBLE_QUOTE_VALUE_ESCAPE,
|
||||
COMMENT,
|
||||
COMMENT_ESCAPE
|
||||
} state = PRE_KEY;
|
||||
|
||||
if (f)
|
||||
r = read_full_stream(f, &contents, NULL);
|
||||
else
|
||||
r = read_full_file(fname, &contents, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (p = contents; *p; p++) {
|
||||
char c = *p;
|
||||
|
||||
switch (state) {
|
||||
|
||||
case PRE_KEY:
|
||||
if (strchr(COMMENTS, c))
|
||||
state = COMMENT;
|
||||
else if (!strchr(WHITESPACE, c)) {
|
||||
state = KEY;
|
||||
last_key_whitespace = (size_t) -1;
|
||||
|
||||
if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
|
||||
return -ENOMEM;
|
||||
|
||||
key[n_key++] = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY:
|
||||
if (strchr(NEWLINE, c)) {
|
||||
state = PRE_KEY;
|
||||
line++;
|
||||
n_key = 0;
|
||||
} else if (c == '=') {
|
||||
state = PRE_VALUE;
|
||||
last_value_whitespace = (size_t) -1;
|
||||
} else {
|
||||
if (!strchr(WHITESPACE, c))
|
||||
last_key_whitespace = (size_t) -1;
|
||||
else if (last_key_whitespace == (size_t) -1)
|
||||
last_key_whitespace = n_key;
|
||||
|
||||
if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
|
||||
return -ENOMEM;
|
||||
|
||||
key[n_key++] = c;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PRE_VALUE:
|
||||
if (strchr(NEWLINE, c)) {
|
||||
state = PRE_KEY;
|
||||
line++;
|
||||
key[n_key] = 0;
|
||||
|
||||
if (value)
|
||||
value[n_value] = 0;
|
||||
|
||||
/* strip trailing whitespace from key */
|
||||
if (last_key_whitespace != (size_t) -1)
|
||||
key[last_key_whitespace] = 0;
|
||||
|
||||
r = push(fname, line, key, value, userdata, n_pushed);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n_key = 0;
|
||||
value = NULL;
|
||||
value_alloc = n_value = 0;
|
||||
|
||||
} else if (c == '\'')
|
||||
state = SINGLE_QUOTE_VALUE;
|
||||
else if (c == '\"')
|
||||
state = DOUBLE_QUOTE_VALUE;
|
||||
else if (c == '\\')
|
||||
state = VALUE_ESCAPE;
|
||||
else if (!strchr(WHITESPACE, c)) {
|
||||
state = VALUE;
|
||||
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
if (strchr(NEWLINE, c)) {
|
||||
state = PRE_KEY;
|
||||
line++;
|
||||
|
||||
key[n_key] = 0;
|
||||
|
||||
if (value)
|
||||
value[n_value] = 0;
|
||||
|
||||
/* Chomp off trailing whitespace from value */
|
||||
if (last_value_whitespace != (size_t) -1)
|
||||
value[last_value_whitespace] = 0;
|
||||
|
||||
/* strip trailing whitespace from key */
|
||||
if (last_key_whitespace != (size_t) -1)
|
||||
key[last_key_whitespace] = 0;
|
||||
|
||||
r = push(fname, line, key, value, userdata, n_pushed);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n_key = 0;
|
||||
value = NULL;
|
||||
value_alloc = n_value = 0;
|
||||
|
||||
} else if (c == '\\') {
|
||||
state = VALUE_ESCAPE;
|
||||
last_value_whitespace = (size_t) -1;
|
||||
} else {
|
||||
if (!strchr(WHITESPACE, c))
|
||||
last_value_whitespace = (size_t) -1;
|
||||
else if (last_value_whitespace == (size_t) -1)
|
||||
last_value_whitespace = n_value;
|
||||
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case VALUE_ESCAPE:
|
||||
state = VALUE;
|
||||
|
||||
if (!strchr(NEWLINE, c)) {
|
||||
/* Escaped newlines we eat up entirely */
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case SINGLE_QUOTE_VALUE:
|
||||
if (c == '\'')
|
||||
state = PRE_VALUE;
|
||||
else if (c == '\\')
|
||||
state = SINGLE_QUOTE_VALUE_ESCAPE;
|
||||
else {
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SINGLE_QUOTE_VALUE_ESCAPE:
|
||||
state = SINGLE_QUOTE_VALUE;
|
||||
|
||||
if (!strchr(NEWLINE, c)) {
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case DOUBLE_QUOTE_VALUE:
|
||||
if (c == '\"')
|
||||
state = PRE_VALUE;
|
||||
else if (c == '\\')
|
||||
state = DOUBLE_QUOTE_VALUE_ESCAPE;
|
||||
else {
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DOUBLE_QUOTE_VALUE_ESCAPE:
|
||||
state = DOUBLE_QUOTE_VALUE;
|
||||
|
||||
if (!strchr(NEWLINE, c)) {
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case COMMENT:
|
||||
if (c == '\\')
|
||||
state = COMMENT_ESCAPE;
|
||||
else if (strchr(NEWLINE, c)) {
|
||||
state = PRE_KEY;
|
||||
line++;
|
||||
}
|
||||
break;
|
||||
|
||||
case COMMENT_ESCAPE:
|
||||
state = COMMENT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (IN_SET(state,
|
||||
PRE_VALUE,
|
||||
VALUE,
|
||||
VALUE_ESCAPE,
|
||||
SINGLE_QUOTE_VALUE,
|
||||
SINGLE_QUOTE_VALUE_ESCAPE,
|
||||
DOUBLE_QUOTE_VALUE,
|
||||
DOUBLE_QUOTE_VALUE_ESCAPE)) {
|
||||
|
||||
key[n_key] = 0;
|
||||
|
||||
if (value)
|
||||
value[n_value] = 0;
|
||||
|
||||
if (state == VALUE)
|
||||
if (last_value_whitespace != (size_t) -1)
|
||||
value[last_value_whitespace] = 0;
|
||||
|
||||
/* strip trailing whitespace from key */
|
||||
if (last_key_whitespace != (size_t) -1)
|
||||
key[last_key_whitespace] = 0;
|
||||
|
||||
r = push(fname, line, key, value, userdata, n_pushed);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
value = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_utf8ness_and_warn(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value) {
|
||||
|
||||
if (!utf8_is_valid(key)) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
p = utf8_escape_invalid(key);
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s:%u: invalid UTF-8 in key '%s', ignoring.",
|
||||
strna(filename), line, p);
|
||||
}
|
||||
|
||||
if (value && !utf8_is_valid(value)) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
p = utf8_escape_invalid(value);
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
|
||||
strna(filename), line, key, p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_env_file_push(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
|
||||
const char *k;
|
||||
va_list aq, *ap = userdata;
|
||||
int r;
|
||||
|
||||
r = check_utf8ness_and_warn(filename, line, key, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
va_copy(aq, *ap);
|
||||
|
||||
while ((k = va_arg(aq, const char *))) {
|
||||
char **v;
|
||||
|
||||
v = va_arg(aq, char **);
|
||||
|
||||
if (streq(key, k)) {
|
||||
va_end(aq);
|
||||
free(*v);
|
||||
*v = value;
|
||||
|
||||
if (n_pushed)
|
||||
(*n_pushed)++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
va_end(aq);
|
||||
free(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_env_filev(
|
||||
FILE *f,
|
||||
const char *fname,
|
||||
va_list ap) {
|
||||
|
||||
int r, n_pushed = 0;
|
||||
va_list aq;
|
||||
|
||||
va_copy(aq, ap);
|
||||
r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed);
|
||||
va_end(aq);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return n_pushed;
|
||||
}
|
||||
|
||||
int parse_env_file_sentinel(
|
||||
FILE *f,
|
||||
const char *fname,
|
||||
...) {
|
||||
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
va_start(ap, fname);
|
||||
r = parse_env_filev(f, fname, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
static int load_env_file_push(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
char ***m = userdata;
|
||||
char *p;
|
||||
int r;
|
||||
|
||||
r = check_utf8ness_and_warn(filename, line, key, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = strjoin(key, "=", value);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_env_replace(m, p);
|
||||
if (r < 0) {
|
||||
free(p);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (n_pushed)
|
||||
(*n_pushed)++;
|
||||
|
||||
free(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_env_file(FILE *f, const char *fname, char ***rl) {
|
||||
char **m = NULL;
|
||||
int r;
|
||||
|
||||
r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL);
|
||||
if (r < 0) {
|
||||
strv_free(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
*rl = m;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_env_file_push_pairs(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
char ***m = userdata;
|
||||
int r;
|
||||
|
||||
r = check_utf8ness_and_warn(filename, line, key, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend(m, key);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!value) {
|
||||
r = strv_extend(m, "");
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
r = strv_push(m, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (n_pushed)
|
||||
(*n_pushed)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_env_file_pairs(FILE *f, const char *fname, char ***rl) {
|
||||
char **m = NULL;
|
||||
int r;
|
||||
|
||||
r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL);
|
||||
if (r < 0) {
|
||||
strv_free(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
*rl = m;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int merge_env_file_push(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
|
||||
char ***env = userdata;
|
||||
char *expanded_value;
|
||||
|
||||
assert(env);
|
||||
|
||||
if (!value) {
|
||||
log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!env_name_is_valid(key)) {
|
||||
log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
|
||||
free(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
expanded_value = replace_env(value, *env,
|
||||
REPLACE_ENV_USE_ENVIRONMENT|
|
||||
REPLACE_ENV_ALLOW_BRACELESS|
|
||||
REPLACE_ENV_ALLOW_EXTENDED);
|
||||
if (!expanded_value)
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(value, expanded_value);
|
||||
|
||||
return load_env_file_push(filename, line, key, value, env, n_pushed);
|
||||
}
|
||||
|
||||
int merge_env_file(
|
||||
char ***env,
|
||||
FILE *f,
|
||||
const char *fname) {
|
||||
|
||||
/* NOTE: this function supports braceful and braceless variable expansions,
|
||||
* plus "extended" substitutions, unlike other exported parsing functions.
|
||||
*/
|
||||
|
||||
return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL);
|
||||
}
|
||||
|
||||
static void write_env_var(FILE *f, const char *v) {
|
||||
const char *p;
|
||||
|
||||
p = strchr(v, '=');
|
||||
if (!p) {
|
||||
/* Fallback */
|
||||
fputs_unlocked(v, f);
|
||||
fputc_unlocked('\n', f);
|
||||
return;
|
||||
}
|
||||
|
||||
p++;
|
||||
fwrite_unlocked(v, 1, p-v, f);
|
||||
|
||||
if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
|
||||
fputc_unlocked('\"', f);
|
||||
|
||||
for (; *p; p++) {
|
||||
if (strchr(SHELL_NEED_ESCAPE, *p))
|
||||
fputc_unlocked('\\', f);
|
||||
|
||||
fputc_unlocked(*p, f);
|
||||
}
|
||||
|
||||
fputc_unlocked('\"', f);
|
||||
} else
|
||||
fputs_unlocked(p, f);
|
||||
|
||||
fputc_unlocked('\n', f);
|
||||
}
|
||||
|
||||
int write_env_file(const char *fname, char **l) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
assert(fname);
|
||||
|
||||
r = fopen_temporary(fname, &f, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
|
||||
(void) fchmod_umask(fileno(f), 0644);
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
write_env_var(f, *i);
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r >= 0) {
|
||||
if (rename(p, fname) >= 0)
|
||||
return 0;
|
||||
|
||||
r = -errno;
|
||||
}
|
||||
|
||||
unlink(p);
|
||||
return r;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
17
src/systemd/src/basic/env-file.h
Normal file
17
src/systemd/src/basic/env-file.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
int parse_env_filev(FILE *f, const char *fname, va_list ap);
|
||||
int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_;
|
||||
#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL)
|
||||
int load_env_file(FILE *f, const char *fname, char ***l);
|
||||
int load_env_file_pairs(FILE *f, const char *fname, char ***l);
|
||||
|
||||
int merge_env_file(char ***env, FILE *f, const char *fname);
|
||||
|
||||
int write_env_file(const char *fname, char **l);
|
|
@ -10,7 +10,7 @@
|
|||
#endif /* NM_IGNORED */
|
||||
|
||||
#include "string-util.h"
|
||||
#include "missing.h"
|
||||
#include "missing_type.h"
|
||||
|
||||
/* What characters are special in the shell? */
|
||||
/* must be escaped outside and inside double-quotes */
|
||||
|
|
|
@ -30,21 +30,15 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR
|
|||
return buffer;
|
||||
}
|
||||
|
||||
int ether_addr_compare(const void *a, const void *b) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
|
||||
return memcmp(a, b, ETH_ALEN);
|
||||
}
|
||||
|
||||
static void ether_addr_hash_func(const void *p, struct siphash *state) {
|
||||
static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
|
||||
siphash24_compress(p, sizeof(struct ether_addr), state);
|
||||
}
|
||||
|
||||
const struct hash_ops ether_addr_hash_ops = {
|
||||
.hash = ether_addr_hash_func,
|
||||
.compare = ether_addr_compare
|
||||
};
|
||||
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
|
||||
|
||||
int ether_addr_from_string(const char *s, struct ether_addr *ret) {
|
||||
size_t pos = 0, n, field;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#define ETHER_ADDR_TO_STRING_MAX (3*6)
|
||||
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
|
||||
|
||||
int ether_addr_compare(const void *a, const void *b);
|
||||
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b);
|
||||
static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
|
||||
return ether_addr_compare(a, b) == 0;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "socket-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "util.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
int close_nointr(int fd) {
|
||||
assert(fd >= 0);
|
||||
|
@ -115,7 +116,7 @@ FILE* safe_fclose(FILE *f) {
|
|||
if (f) {
|
||||
PROTECT_ERRNO;
|
||||
|
||||
assert_se(fclose_nointr(f) != EBADF);
|
||||
assert_se(fclose_nointr(f) != -EBADF);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -653,7 +654,7 @@ int fd_duplicate_data_fd(int fd) {
|
|||
|
||||
if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
|
||||
|
||||
r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size);
|
||||
r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
|
||||
if (r < 0 && r != -EAGAIN)
|
||||
return r; /* If we get EAGAIN it could be because of the source or because of
|
||||
* the destination fd, we can't know, as sendfile() and friends won't
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -44,16 +44,6 @@ int read_full_stream(FILE *f, char **contents, size_t *size);
|
|||
|
||||
int verify_file(const char *fn, const char *blob, bool accept_extra_nl);
|
||||
|
||||
int parse_env_filev(FILE *f, const char *fname, va_list ap);
|
||||
int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_;
|
||||
#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL)
|
||||
int load_env_file(FILE *f, const char *fname, char ***l);
|
||||
int load_env_file_pairs(FILE *f, const char *fname, char ***l);
|
||||
|
||||
int merge_env_file(char ***env, FILE *f, const char *fname);
|
||||
|
||||
int write_env_file(const char *fname, char **l);
|
||||
|
||||
int executable_is_script(const char *path, char **interpreter);
|
||||
|
||||
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
|
||||
|
@ -66,27 +56,23 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
|
|||
int fflush_and_check(FILE *f);
|
||||
int fflush_sync_and_check(FILE *f);
|
||||
|
||||
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
|
||||
int mkostemp_safe(char *pattern);
|
||||
int fmkostemp_safe(char *pattern, const char *mode, FILE**_f);
|
||||
|
||||
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
|
||||
int tempfn_random(const char *p, const char *extra, char **ret);
|
||||
int tempfn_random_child(const char *p, const char *extra, char **ret);
|
||||
|
||||
int write_timestamp_file_atomic(const char *fn, usec_t n);
|
||||
int read_timestamp_file(const char *fn, usec_t *ret);
|
||||
|
||||
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
|
||||
|
||||
int open_tmpfile_unlinkable(const char *directory, int flags);
|
||||
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
|
||||
int open_serialization_fd(const char *ident);
|
||||
typedef enum ReadLineFlags {
|
||||
READ_LINE_ONLY_NUL = 1 << 0,
|
||||
} ReadLineFlags;
|
||||
|
||||
int link_tmpfile(int fd, const char *path, const char *target);
|
||||
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret);
|
||||
|
||||
int read_nul_string(FILE *f, char **ret);
|
||||
static inline int read_line(FILE *f, size_t limit, char **ret) {
|
||||
return read_line_full(f, limit, 0, ret);
|
||||
}
|
||||
|
||||
int mkdtemp_malloc(const char *template, char **ret);
|
||||
static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
|
||||
return read_line_full(f, limit, READ_LINE_ONLY_NUL, ret);
|
||||
}
|
||||
|
||||
int read_line(FILE *f, size_t limit, char **ret);
|
||||
int safe_fgetc(FILE *f, char *ret);
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
#include "alloc-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "locale-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing.h"
|
||||
|
@ -29,6 +29,7 @@
|
|||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
|
||||
|
@ -216,31 +217,62 @@ 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;
|
||||
assert(path);
|
||||
|
||||
/* Under the assumption that we are running privileged we
|
||||
* first change the access mode and only then hand out
|
||||
/* 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 (chmod(path, mode) < 0)
|
||||
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) {
|
||||
struct stat st;
|
||||
|
||||
if (stat(fd_path, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chmod(fd_path, mode & 07777) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (uid != UID_INVALID || gid != GID_INVALID)
|
||||
if (chown(path, uid, gid) < 0)
|
||||
if (chown(fd_path, uid, gid) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
|
||||
/* Under the assumption that we are running privileged we
|
||||
* first change the access mode and only then hand out
|
||||
/* 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 (fchmod(fd, mode) < 0)
|
||||
if (mode != MODE_INVALID) {
|
||||
|
||||
if ((mode & S_IFMT) != 0) {
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fchmod(fd, mode & 0777) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (uid != UID_INVALID || gid != GID_INVALID)
|
||||
if (fchown(fd, uid, gid) < 0)
|
||||
|
@ -270,7 +302,6 @@ int fchmod_opath(int fd, mode_t m) {
|
|||
* fchownat() does. */
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
|
||||
if (chmod(procfs_path, m) < 0)
|
||||
return -errno;
|
||||
|
||||
|
@ -642,15 +673,42 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
|
|||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
static bool safe_transition(const struct stat *a, const struct stat *b) {
|
||||
static bool unsafe_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 false;
|
||||
|
||||
return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
|
||||
return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */
|
||||
}
|
||||
|
||||
static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
|
||||
_cleanup_free_ char *n1 = NULL, *n2 = NULL;
|
||||
|
||||
if (!FLAGS_SET(flags, CHASE_WARN))
|
||||
return -ENOLINK;
|
||||
|
||||
(void) fd_get_path(a, &n1);
|
||||
(void) fd_get_path(b, &n2);
|
||||
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
|
||||
"Detected unsafe path transition %s %s %s during canonicalization of %s.",
|
||||
n1, special_glyph(SPECIAL_GLYPH_ARROW), n2, path);
|
||||
}
|
||||
|
||||
static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
|
||||
_cleanup_free_ char *n1 = NULL;
|
||||
|
||||
if (!FLAGS_SET(flags, CHASE_WARN))
|
||||
return -EREMOTE;
|
||||
|
||||
(void) fd_get_path(fd, &n1);
|
||||
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE),
|
||||
"Detected autofs mount point %s during canonicalization of %s.",
|
||||
n1, path);
|
||||
}
|
||||
|
||||
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
|
||||
|
@ -713,6 +771,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
* path is fully normalized, and == 0 for each normalization step. This may be combined with
|
||||
* CHASE_NONEXISTENT, in which case 1 is returned when a component is not found.
|
||||
*
|
||||
* 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from
|
||||
* unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If
|
||||
* CHASE_WARN is also set a warning describing the unsafe transition is emitted.
|
||||
*
|
||||
* 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, the path normalization is
|
||||
* aborted and -EREMOTE is returned. If CHASE_WARN is also set a warning showing the path of the mount point
|
||||
* is emitted.
|
||||
*
|
||||
* */
|
||||
|
||||
/* A root directory of "/" or "" is identical to none */
|
||||
|
@ -827,8 +893,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
if (fstat(fd_parent, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!safe_transition(&previous_stat, &st))
|
||||
return -EPERM;
|
||||
if (unsafe_transition(&previous_stat, &st))
|
||||
return log_unsafe_transition(fd, fd_parent, path, flags);
|
||||
|
||||
previous_stat = st;
|
||||
}
|
||||
|
@ -868,14 +934,14 @@ 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;
|
||||
unsafe_transition(&previous_stat, &st))
|
||||
return log_unsafe_transition(fd, child, path, flags);
|
||||
|
||||
previous_stat = st;
|
||||
|
||||
if ((flags & CHASE_NO_AUTOFS) &&
|
||||
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
|
||||
return -EREMOTE;
|
||||
return log_autofs_mount_point(child, path, flags);
|
||||
|
||||
if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
|
||||
char *joined;
|
||||
|
@ -907,8 +973,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!safe_transition(&previous_stat, &st))
|
||||
return -EPERM;
|
||||
if (unsafe_transition(&previous_stat, &st))
|
||||
return log_unsafe_transition(child, fd, path, flags);
|
||||
|
||||
previous_stat = st;
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ enum {
|
|||
CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */
|
||||
CHASE_STEP = 1 << 6, /* If set, just execute a single step of the normalization */
|
||||
CHASE_NOFOLLOW = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */
|
||||
CHASE_WARN = 1 << 8, /* Emit an appropriate warning when an error is encountered */
|
||||
};
|
||||
|
||||
/* How many iterations to execute before returning -ELOOP */
|
||||
|
|
|
@ -7,22 +7,14 @@
|
|||
#include "hash-funcs.h"
|
||||
#include "path-util.h"
|
||||
|
||||
void string_hash_func(const void *p, struct siphash *state) {
|
||||
void string_hash_func(const char *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);
|
||||
}
|
||||
DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func);
|
||||
|
||||
const struct hash_ops string_hash_ops = {
|
||||
.hash = string_hash_func,
|
||||
.compare = string_compare_func
|
||||
};
|
||||
|
||||
void path_hash_func(const void *p, struct siphash *state) {
|
||||
const char *q = p;
|
||||
void path_hash_func(const char *q, struct siphash *state) {
|
||||
size_t n;
|
||||
|
||||
assert(q);
|
||||
|
@ -60,14 +52,11 @@ void path_hash_func(const void *p, struct siphash *state) {
|
|||
}
|
||||
}
|
||||
|
||||
int path_compare_func(const void *a, const void *b) {
|
||||
int path_compare_func(const char *a, const char *b) {
|
||||
return path_compare(a, b);
|
||||
}
|
||||
|
||||
const struct hash_ops path_hash_ops = {
|
||||
.hash = path_hash_func,
|
||||
.compare = path_compare_func
|
||||
};
|
||||
DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare_func);
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
void trivial_hash_func(const void *p, struct siphash *state) {
|
||||
|
@ -80,41 +69,29 @@ int trivial_compare_func(const void *a, const void *b) {
|
|||
|
||||
const struct hash_ops trivial_hash_ops = {
|
||||
.hash = trivial_hash_func,
|
||||
.compare = trivial_compare_func
|
||||
.compare = trivial_compare_func,
|
||||
};
|
||||
|
||||
void uint64_hash_func(const void *p, struct siphash *state) {
|
||||
void uint64_hash_func(const uint64_t *p, struct siphash *state) {
|
||||
siphash24_compress(p, sizeof(uint64_t), state);
|
||||
}
|
||||
|
||||
int uint64_compare_func(const void *_a, const void *_b) {
|
||||
uint64_t a, b;
|
||||
a = *(const uint64_t*) _a;
|
||||
b = *(const uint64_t*) _b;
|
||||
return CMP(a, b);
|
||||
int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
|
||||
return CMP(*a, *b);
|
||||
}
|
||||
|
||||
const struct hash_ops uint64_hash_ops = {
|
||||
.hash = uint64_hash_func,
|
||||
.compare = uint64_compare_func
|
||||
};
|
||||
DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func);
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
#if SIZEOF_DEV_T != 8
|
||||
void devt_hash_func(const void *p, struct siphash *state) {
|
||||
void devt_hash_func(const dev_t *p, struct siphash *state) {
|
||||
siphash24_compress(p, sizeof(dev_t), state);
|
||||
}
|
||||
|
||||
int devt_compare_func(const void *_a, const void *_b) {
|
||||
dev_t a, b;
|
||||
a = *(const dev_t*) _a;
|
||||
b = *(const dev_t*) _b;
|
||||
return CMP(a, b);
|
||||
int devt_compare_func(const dev_t *a, const dev_t *b) {
|
||||
return CMP(*a, *b);
|
||||
}
|
||||
|
||||
const struct hash_ops devt_hash_ops = {
|
||||
.hash = devt_hash_func,
|
||||
.compare = devt_compare_func
|
||||
};
|
||||
DEFINE_HASH_OPS(devt_hash_ops, dev_t, devt_hash_func, devt_compare_func);
|
||||
#endif
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "macro.h"
|
||||
#include "siphash24.h"
|
||||
|
||||
|
@ -10,14 +11,74 @@ typedef int (*compare_func_t)(const void *a, const void *b);
|
|||
struct hash_ops {
|
||||
hash_func_t hash;
|
||||
compare_func_t compare;
|
||||
free_func_t free_key;
|
||||
free_func_t free_value;
|
||||
};
|
||||
|
||||
void string_hash_func(const void *p, struct siphash *state);
|
||||
int string_compare_func(const void *a, const void *b) _pure_;
|
||||
#define _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, free_key_func, free_value_func, scope) \
|
||||
_unused_ static void (* UNIQ_T(static_hash_wrapper, uq))(const type *, struct siphash *) = hash_func; \
|
||||
_unused_ static int (* UNIQ_T(static_compare_wrapper, uq))(const type *, const type *) = compare_func; \
|
||||
scope const struct hash_ops name = { \
|
||||
.hash = (hash_func_t) hash_func, \
|
||||
.compare = (compare_func_t) compare_func, \
|
||||
.free_key = free_key_func, \
|
||||
.free_value = free_value_func, \
|
||||
}
|
||||
|
||||
#define _DEFINE_FREE_FUNC(uq, type, wrapper_name, func) \
|
||||
/* Type-safe free function */ \
|
||||
static void UNIQ_T(wrapper_name, uq)(void *a) { \
|
||||
type *_a = a; \
|
||||
func(_a); \
|
||||
}
|
||||
|
||||
#define _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(uq, name, type, hash_func, compare_func, free_func, scope) \
|
||||
_DEFINE_FREE_FUNC(uq, type, static_free_wrapper, free_func); \
|
||||
_DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \
|
||||
UNIQ_T(static_free_wrapper, uq), NULL, scope)
|
||||
|
||||
#define _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(uq, name, type, hash_func, compare_func, type_value, free_func, scope) \
|
||||
_DEFINE_FREE_FUNC(uq, type_value, static_free_wrapper, free_func); \
|
||||
_DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \
|
||||
NULL, UNIQ_T(static_free_wrapper, uq), scope)
|
||||
|
||||
#define _DEFINE_HASH_OPS_FULL(uq, name, type, hash_func, compare_func, free_key_func, type_value, free_value_func, scope) \
|
||||
_DEFINE_FREE_FUNC(uq, type, static_free_key_wrapper, free_key_func); \
|
||||
_DEFINE_FREE_FUNC(uq, type_value, static_free_value_wrapper, free_value_func); \
|
||||
_DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \
|
||||
UNIQ_T(static_free_key_wrapper, uq), \
|
||||
UNIQ_T(static_free_value_wrapper, uq), scope)
|
||||
|
||||
#define DEFINE_HASH_OPS(name, type, hash_func, compare_func) \
|
||||
_DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL,)
|
||||
|
||||
#define DEFINE_PRIVATE_HASH_OPS(name, type, hash_func, compare_func) \
|
||||
_DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL, static)
|
||||
|
||||
#define DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \
|
||||
_DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func,)
|
||||
|
||||
#define DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \
|
||||
_DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func, static)
|
||||
|
||||
#define DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \
|
||||
_DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func,)
|
||||
|
||||
#define DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \
|
||||
_DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func, static)
|
||||
|
||||
#define DEFINE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \
|
||||
_DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func,)
|
||||
|
||||
#define DEFINE_PRIVATE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \
|
||||
_DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func, static)
|
||||
|
||||
void string_hash_func(const char *p, struct siphash *state);
|
||||
#define string_compare_func strcmp
|
||||
extern const struct hash_ops string_hash_ops;
|
||||
|
||||
void path_hash_func(const void *p, struct siphash *state);
|
||||
int path_compare_func(const void *a, const void *b) _pure_;
|
||||
void path_hash_func(const char *p, struct siphash *state);
|
||||
int path_compare_func(const char *a, const char *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
|
||||
|
@ -28,15 +89,15 @@ 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. */
|
||||
void uint64_hash_func(const void *p, struct siphash *state);
|
||||
int uint64_compare_func(const void *a, const void *b) _pure_;
|
||||
void uint64_hash_func(const uint64_t *p, struct siphash *state);
|
||||
int uint64_compare_func(const uint64_t *a, const uint64_t *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! */
|
||||
#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_;
|
||||
void devt_hash_func(const dev_t *p, struct siphash *state) _pure_;
|
||||
int devt_compare_func(const dev_t *a, const dev_t *b) _pure_;
|
||||
extern const struct hash_ops devt_hash_ops;
|
||||
#else
|
||||
#define devt_hash_func uint64_hash_func
|
||||
|
|
|
@ -278,7 +278,7 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
|
|||
};
|
||||
|
||||
#if VALGRIND
|
||||
__attribute__((destructor)) static void cleanup_pools(void) {
|
||||
_destructor_ static void cleanup_pools(void) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
int r;
|
||||
|
||||
|
@ -781,7 +781,7 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu
|
|||
|
||||
h->type = type;
|
||||
h->from_pool = up;
|
||||
h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
|
||||
h->hash_ops = hash_ops ?: &trivial_hash_ops;
|
||||
|
||||
if (type == HASHMAP_TYPE_ORDERED) {
|
||||
OrderedHashmap *lh = (OrderedHashmap*)h;
|
||||
|
@ -850,7 +850,7 @@ int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASH
|
|||
|
||||
static void hashmap_free_no_clear(HashmapBase *h) {
|
||||
assert(!h->has_indirect);
|
||||
assert(!h->n_direct_entries);
|
||||
assert(h->n_direct_entries == 0);
|
||||
|
||||
#if ENABLE_DEBUG_HASHMAP
|
||||
assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
|
||||
|
@ -866,47 +866,42 @@ static void hashmap_free_no_clear(HashmapBase *h) {
|
|||
free(h);
|
||||
}
|
||||
|
||||
HashmapBase *internal_hashmap_free(HashmapBase *h) {
|
||||
|
||||
/* Free the hashmap, but nothing in it */
|
||||
|
||||
HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
|
||||
if (h) {
|
||||
internal_hashmap_clear(h);
|
||||
internal_hashmap_clear(h, default_free_key, default_free_value);
|
||||
hashmap_free_no_clear(h);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
|
||||
|
||||
/* Free the hashmap and all data objects in it, but not the
|
||||
* keys */
|
||||
|
||||
if (h) {
|
||||
internal_hashmap_clear_free(h);
|
||||
hashmap_free_no_clear(h);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Hashmap *hashmap_free_free_free(Hashmap *h) {
|
||||
|
||||
/* Free the hashmap and all data and key objects in it */
|
||||
|
||||
if (h) {
|
||||
hashmap_clear_free_free(h);
|
||||
hashmap_free_no_clear(HASHMAP_BASE(h));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void internal_hashmap_clear(HashmapBase *h) {
|
||||
void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
|
||||
free_func_t free_key, free_value;
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
free_key = h->hash_ops->free_key ?: default_free_key;
|
||||
free_value = h->hash_ops->free_value ?: default_free_value;
|
||||
|
||||
if (free_key || free_value) {
|
||||
|
||||
/* If destructor calls are defined, let's destroy things defensively: let's take the item out of the
|
||||
* hash table, and only then call the destructor functions. If these destructors then try to unregister
|
||||
* themselves from our hash table a second time, the entry is already gone. */
|
||||
|
||||
while (internal_hashmap_size(h) > 0) {
|
||||
void *v, *k;
|
||||
|
||||
v = internal_hashmap_first_key_and_value(h, true, &k);
|
||||
|
||||
if (free_key)
|
||||
free_key(k);
|
||||
|
||||
if (free_value)
|
||||
free_value(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (h->has_indirect) {
|
||||
free(h->indirect.storage);
|
||||
h->has_indirect = false;
|
||||
|
@ -923,35 +918,6 @@ void internal_hashmap_clear(HashmapBase *h) {
|
|||
base_set_dirty(h);
|
||||
}
|
||||
|
||||
void internal_hashmap_clear_free(HashmapBase *h) {
|
||||
unsigned idx;
|
||||
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
|
||||
idx = skip_free_buckets(h, idx + 1))
|
||||
free(entry_value(h, bucket_at(h, idx)));
|
||||
|
||||
internal_hashmap_clear(h);
|
||||
}
|
||||
|
||||
void hashmap_clear_free_free(Hashmap *h) {
|
||||
unsigned idx;
|
||||
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
|
||||
idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
|
||||
struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
|
||||
free((void*)e->b.key);
|
||||
free(e->value);
|
||||
}
|
||||
|
||||
internal_hashmap_clear(HASHMAP_BASE(h));
|
||||
}
|
||||
|
||||
static int resize_buckets(HashmapBase *h, unsigned entries_add);
|
||||
|
||||
/*
|
||||
|
@ -1515,8 +1481,8 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_
|
|||
return 0;
|
||||
}
|
||||
|
||||
void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
|
||||
struct plain_hashmap_entry *e;
|
||||
void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
|
||||
struct hashmap_base_entry *e;
|
||||
unsigned hash, idx;
|
||||
|
||||
if (!h)
|
||||
|
@ -1527,8 +1493,8 @@ void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
|
|||
if (idx == IDX_NIL)
|
||||
return NULL;
|
||||
|
||||
e = plain_bucket_at(h, idx);
|
||||
if (e->value != value)
|
||||
e = bucket_at(h, idx);
|
||||
if (entry_value(h, e) != value)
|
||||
return NULL;
|
||||
|
||||
remove_entry(h, idx);
|
||||
|
@ -1742,7 +1708,7 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) {
|
|||
}
|
||||
|
||||
if (r < 0) {
|
||||
internal_hashmap_free(copy);
|
||||
internal_hashmap_free(copy, false, false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#define HASH_KEY_SIZE 16
|
||||
|
||||
typedef void* (*hashmap_destroy_t)(void *p);
|
||||
|
||||
/* The base type for all hashmap and set types. Many functions in the
|
||||
* implementation take (HashmapBase*) parameters and are run-time polymorphic,
|
||||
* though the API is not meant to be polymorphic (do not call functions
|
||||
|
@ -87,25 +89,33 @@ OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HA
|
|||
#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
|
||||
#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
|
||||
|
||||
HashmapBase *internal_hashmap_free(HashmapBase *h);
|
||||
HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
|
||||
static inline Hashmap *hashmap_free(Hashmap *h) {
|
||||
return (void*)internal_hashmap_free(HASHMAP_BASE(h));
|
||||
return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
|
||||
}
|
||||
static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
|
||||
return (void*)internal_hashmap_free(HASHMAP_BASE(h));
|
||||
return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
|
||||
}
|
||||
|
||||
HashmapBase *internal_hashmap_free_free(HashmapBase *h);
|
||||
static inline Hashmap *hashmap_free_free(Hashmap *h) {
|
||||
return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
|
||||
return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
|
||||
}
|
||||
static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
|
||||
return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
|
||||
return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
|
||||
}
|
||||
|
||||
Hashmap *hashmap_free_free_free(Hashmap *h);
|
||||
static inline Hashmap *hashmap_free_free_key(Hashmap *h) {
|
||||
return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
|
||||
}
|
||||
static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) {
|
||||
return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
|
||||
}
|
||||
|
||||
static inline Hashmap *hashmap_free_free_free(Hashmap *h) {
|
||||
return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
|
||||
}
|
||||
static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
|
||||
return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
|
||||
return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
|
||||
}
|
||||
|
||||
IteratedCache *iterated_cache_free(IteratedCache *cache);
|
||||
|
@ -181,7 +191,11 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key,
|
|||
return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
|
||||
}
|
||||
|
||||
void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
|
||||
void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value);
|
||||
static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
|
||||
return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value);
|
||||
}
|
||||
|
||||
static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
|
||||
return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
|
||||
}
|
||||
|
@ -258,25 +272,33 @@ static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void
|
|||
return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
|
||||
}
|
||||
|
||||
void internal_hashmap_clear(HashmapBase *h);
|
||||
void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
|
||||
static inline void hashmap_clear(Hashmap *h) {
|
||||
internal_hashmap_clear(HASHMAP_BASE(h));
|
||||
internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
|
||||
}
|
||||
static inline void ordered_hashmap_clear(OrderedHashmap *h) {
|
||||
internal_hashmap_clear(HASHMAP_BASE(h));
|
||||
internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
|
||||
}
|
||||
|
||||
void internal_hashmap_clear_free(HashmapBase *h);
|
||||
static inline void hashmap_clear_free(Hashmap *h) {
|
||||
internal_hashmap_clear_free(HASHMAP_BASE(h));
|
||||
internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
|
||||
}
|
||||
static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
|
||||
internal_hashmap_clear_free(HASHMAP_BASE(h));
|
||||
internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
|
||||
}
|
||||
|
||||
void hashmap_clear_free_free(Hashmap *h);
|
||||
static inline void hashmap_clear_free_key(Hashmap *h) {
|
||||
internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
|
||||
}
|
||||
static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) {
|
||||
internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
|
||||
}
|
||||
|
||||
static inline void hashmap_clear_free_free(Hashmap *h) {
|
||||
internal_hashmap_clear(HASHMAP_BASE(h), free, free);
|
||||
}
|
||||
static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
|
||||
hashmap_clear_free_free(PLAIN_HASHMAP(h));
|
||||
internal_hashmap_clear(HASHMAP_BASE(h), free, free);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -73,12 +73,12 @@ int gethostname_strict(char **ret) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool hostname_valid_char(char c) {
|
||||
bool valid_ldh_char(char c) {
|
||||
return
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
IN_SET(c, '-', '_', '.');
|
||||
c == '-';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,7 +94,7 @@ static bool hostname_valid_char(char c) {
|
|||
bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
|
||||
unsigned n_dots = 0;
|
||||
const char *p;
|
||||
bool dot;
|
||||
bool dot, hyphen;
|
||||
|
||||
if (isempty(s))
|
||||
return false;
|
||||
|
@ -104,23 +104,34 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
|
|||
* sequence. Also ensures that the length stays below
|
||||
* HOST_NAME_MAX. */
|
||||
|
||||
for (p = s, dot = true; *p; p++) {
|
||||
for (p = s, dot = hyphen = true; *p; p++)
|
||||
if (*p == '.') {
|
||||
if (dot)
|
||||
if (dot || hyphen)
|
||||
return false;
|
||||
|
||||
dot = true;
|
||||
hyphen = false;
|
||||
n_dots++;
|
||||
} else {
|
||||
if (!hostname_valid_char(*p))
|
||||
|
||||
} else if (*p == '-') {
|
||||
if (dot)
|
||||
return false;
|
||||
|
||||
dot = false;
|
||||
hyphen = true;
|
||||
|
||||
} else {
|
||||
if (!valid_ldh_char(*p))
|
||||
return false;
|
||||
|
||||
dot = false;
|
||||
hyphen = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dot && (n_dots < 2 || !allow_trailing_dot))
|
||||
return false;
|
||||
if (hyphen)
|
||||
return false;
|
||||
|
||||
if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
|
||||
* Linux, but DNS allows domain names
|
||||
|
@ -132,29 +143,38 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
|
|||
|
||||
char* hostname_cleanup(char *s) {
|
||||
char *p, *d;
|
||||
bool dot;
|
||||
bool dot, hyphen;
|
||||
|
||||
assert(s);
|
||||
|
||||
strshorten(s, HOST_NAME_MAX);
|
||||
|
||||
for (p = s, d = s, dot = true; *p; p++) {
|
||||
for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++)
|
||||
if (*p == '.') {
|
||||
if (dot)
|
||||
if (dot || hyphen)
|
||||
continue;
|
||||
|
||||
*(d++) = '.';
|
||||
dot = true;
|
||||
} else if (hostname_valid_char(*p)) {
|
||||
hyphen = false;
|
||||
|
||||
} else if (*p == '-') {
|
||||
if (dot)
|
||||
continue;
|
||||
|
||||
*(d++) = '-';
|
||||
dot = false;
|
||||
hyphen = true;
|
||||
|
||||
} else if (valid_ldh_char(*p)) {
|
||||
*(d++) = *p;
|
||||
dot = false;
|
||||
hyphen = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dot && d > s)
|
||||
d[-1] = 0;
|
||||
else
|
||||
*d = 0;
|
||||
if (d > s && IN_SET(d[-1], '-', '.'))
|
||||
/* The dot can occur at most once, but we might have multiple
|
||||
* hyphens, hence the loop */
|
||||
d--;
|
||||
*d = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ bool hostname_is_set(void);
|
|||
char* gethostname_malloc(void);
|
||||
int gethostname_strict(char **ret);
|
||||
|
||||
bool valid_ldh_char(char c) _const_;
|
||||
bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
|
||||
char* hostname_cleanup(char *s);
|
||||
|
||||
|
|
|
@ -366,7 +366,7 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u
|
|||
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
|
||||
assert(addr);
|
||||
|
||||
return 32 - u32ctz(be32toh(addr->s_addr));
|
||||
return 32U - u32ctz(be32toh(addr->s_addr));
|
||||
}
|
||||
|
||||
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
|
||||
|
@ -608,15 +608,12 @@ int in_addr_prefix_from_string_auto_internal(
|
|||
|
||||
}
|
||||
|
||||
void in_addr_data_hash_func(const void *p, struct siphash *state) {
|
||||
const struct in_addr_data *a = p;
|
||||
|
||||
static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
|
||||
siphash24_compress(&a->family, sizeof(a->family), state);
|
||||
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
|
||||
}
|
||||
|
||||
int in_addr_data_compare_func(const void *a, const void *b) {
|
||||
const struct in_addr_data *x = a, *y = b;
|
||||
static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
|
||||
int r;
|
||||
|
||||
r = CMP(x->family, y->family);
|
||||
|
@ -626,8 +623,5 @@ int in_addr_data_compare_func(const void *a, const void *b) {
|
|||
return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
|
||||
}
|
||||
|
||||
const struct hash_ops in_addr_data_hash_ops = {
|
||||
.hash = in_addr_data_hash_func,
|
||||
.compare = in_addr_data_compare_func,
|
||||
};
|
||||
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
@ -69,6 +69,4 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
|
|||
* See also oss-fuzz#11344. */
|
||||
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
|
||||
|
||||
void in_addr_data_hash_func(const void *p, struct siphash *state);
|
||||
int in_addr_data_compare_func(const void *a, const void *b);
|
||||
extern const struct hash_ops in_addr_data_hash_ops;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* The head of the linked list. Use this in the structure that shall
|
||||
* contain the head of the linked list */
|
||||
#define LIST_HEAD(t,name) \
|
||||
|
@ -13,8 +15,8 @@
|
|||
/* Initialize the list's head */
|
||||
#define LIST_HEAD_INIT(head) \
|
||||
do { \
|
||||
(head) = NULL; } \
|
||||
while (false)
|
||||
(head) = NULL; \
|
||||
} while (false)
|
||||
|
||||
/* Initialize a list item */
|
||||
#define LIST_INIT(name,item) \
|
||||
|
|
|
@ -2,33 +2,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define _printf_(a, b) __attribute__ ((__format__(printf, a, b)))
|
||||
#define _printf_(a, b) __attribute__((__format__(printf, a, b)))
|
||||
#ifdef __clang__
|
||||
# define _alloc_(...)
|
||||
#else
|
||||
# define _alloc_(...) __attribute__ ((__alloc_size__(__VA_ARGS__)))
|
||||
# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
|
||||
#endif
|
||||
#define _sentinel_ __attribute__ ((__sentinel__))
|
||||
#define _unused_ __attribute__ ((__unused__))
|
||||
#define _destructor_ __attribute__ ((__destructor__))
|
||||
#define _pure_ __attribute__ ((__pure__))
|
||||
#define _const_ __attribute__ ((__const__))
|
||||
#define _deprecated_ __attribute__ ((__deprecated__))
|
||||
#define _packed_ __attribute__ ((__packed__))
|
||||
#define _malloc_ __attribute__ ((__malloc__))
|
||||
#define _weak_ __attribute__ ((__weak__))
|
||||
#define _sentinel_ __attribute__((__sentinel__))
|
||||
#define _section_(x) __attribute__((__section__(x)))
|
||||
#define _used_ __attribute__((__used__))
|
||||
#define _unused_ __attribute__((__unused__))
|
||||
#define _destructor_ __attribute__((__destructor__))
|
||||
#define _pure_ __attribute__((__pure__))
|
||||
#define _const_ __attribute__((__const__))
|
||||
#define _deprecated_ __attribute__((__deprecated__))
|
||||
#define _packed_ __attribute__((__packed__))
|
||||
#define _malloc_ __attribute__((__malloc__))
|
||||
#define _weak_ __attribute__((__weak__))
|
||||
#define _likely_(x) (__builtin_expect(!!(x), 1))
|
||||
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
|
||||
#define _public_ __attribute__ ((__visibility__("default")))
|
||||
#define _hidden_ __attribute__ ((__visibility__("hidden")))
|
||||
#define _public_ __attribute__((__visibility__("default")))
|
||||
#define _hidden_ __attribute__((__visibility__("hidden")))
|
||||
#define _weakref_(x) __attribute__((__weakref__(#x)))
|
||||
#define _align_(x) __attribute__((__aligned__(x)))
|
||||
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
|
||||
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
|
||||
#define _cleanup_(x) __attribute__((__cleanup__(x)))
|
||||
#if __GNUC__ >= 7
|
||||
#define _fallthrough_ __attribute__((__fallthrough__))
|
||||
|
@ -56,6 +61,29 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
|
||||
# ifdef __SANITIZE_ADDRESS__
|
||||
# define HAS_FEATURE_ADDRESS_SANITIZER 1
|
||||
# elif defined(__has_feature)
|
||||
# if __has_feature(address_sanitizer)
|
||||
# define HAS_FEATURE_ADDRESS_SANITIZER 1
|
||||
# endif
|
||||
# endif
|
||||
# if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
|
||||
# define HAS_FEATURE_ADDRESS_SANITIZER 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global
|
||||
* variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since
|
||||
* our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be
|
||||
* padded, but GCC doesn't pad those anyway if AddressSanitizer is enabled. */
|
||||
#if HAS_FEATURE_ADDRESS_SANITIZER && defined(__clang__)
|
||||
#define _variable_no_sanitize_address_ __attribute__((__no_sanitize_address__))
|
||||
#else
|
||||
#define _variable_no_sanitize_address_
|
||||
#endif
|
||||
|
||||
#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__)
|
||||
/* Temporarily disable some warnings */
|
||||
#define DISABLE_WARNING_FORMAT_NONLITERAL \
|
||||
|
@ -255,11 +283,12 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
|
|||
* computation should be possible in the given type. Therefore, we use
|
||||
* [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
|
||||
* quotient and the remainder, so both should be equally fast. */
|
||||
#define DIV_ROUND_UP(_x, _y) \
|
||||
#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y))
|
||||
#define __DIV_ROUND_UP(xq, x, yq, y) \
|
||||
({ \
|
||||
const typeof(_x) __x = (_x); \
|
||||
const typeof(_y) __y = (_y); \
|
||||
(__x / __y + !!(__x % __y)); \
|
||||
const typeof(x) UNIQ_T(X, xq) = (x); \
|
||||
const typeof(y) UNIQ_T(Y, yq) = (y); \
|
||||
(UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \
|
||||
})
|
||||
|
||||
#ifdef __COVERITY__
|
||||
|
@ -466,6 +495,11 @@ static inline int __coverity_check__(int condition) {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \
|
||||
static inline void name(type *p) { \
|
||||
func(p); \
|
||||
}
|
||||
|
||||
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
|
||||
static inline void func##p(type *p) { \
|
||||
if (*p) \
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/oom.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -18,10 +19,12 @@
|
|||
#include "missing.h"
|
||||
#include "parse-util.h"
|
||||
#include "process-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
int parse_boolean(const char *v) {
|
||||
assert(v);
|
||||
if (!v)
|
||||
return -EINVAL;
|
||||
|
||||
if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
|
||||
return 1;
|
||||
|
@ -715,18 +718,51 @@ int parse_ip_port(const char *s, uint16_t *ret) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
|
||||
unsigned l, h;
|
||||
int r;
|
||||
|
||||
r = parse_range(s, &l, &h);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (l <= 0 || l > 65535 || h <= 0 || h > 65535)
|
||||
return -EINVAL;
|
||||
|
||||
if (h < l)
|
||||
return -EINVAL;
|
||||
|
||||
*low = l;
|
||||
*high = h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_dev(const char *s, dev_t *ret) {
|
||||
const char *major;
|
||||
unsigned x, y;
|
||||
dev_t d;
|
||||
size_t n;
|
||||
int r;
|
||||
|
||||
if (sscanf(s, "%u:%u", &x, &y) != 2)
|
||||
n = strspn(s, DIGITS);
|
||||
if (n == 0)
|
||||
return -EINVAL;
|
||||
if (s[n] != ':')
|
||||
return -EINVAL;
|
||||
|
||||
d = makedev(x, y);
|
||||
if ((unsigned) major(d) != x || (unsigned) minor(d) != y)
|
||||
return -EINVAL;
|
||||
major = strndupa(s, n);
|
||||
r = safe_atou(major, &x);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = d;
|
||||
r = safe_atou(s + n + 1, &y);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
|
||||
return -ERANGE;
|
||||
|
||||
*ret = makedev(x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -115,5 +115,6 @@ int parse_permille(const char *p);
|
|||
int parse_nice(const char *p, int *ret);
|
||||
|
||||
int parse_ip_port(const char *s, uint16_t *ret);
|
||||
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
|
||||
|
||||
int parse_oom_score_adjust(const char *s, int *ret);
|
||||
|
|
|
@ -113,7 +113,7 @@ int path_make_absolute_cwd(const char *p, char **ret) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c = path_join(NULL, cwd, p);
|
||||
c = path_join(cwd, p);
|
||||
}
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
@ -486,18 +486,62 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) {
|
|||
return path_equal(a, b) || files_same(a, b, flags) > 0;
|
||||
}
|
||||
|
||||
char* path_join(const char *root, const char *path, const char *rest) {
|
||||
assert(path);
|
||||
char* path_join_internal(const char *first, ...) {
|
||||
char *joined, *q;
|
||||
const char *p;
|
||||
va_list ap;
|
||||
bool slash;
|
||||
size_t sz;
|
||||
|
||||
if (!isempty(root))
|
||||
return strjoin(root, endswith(root, "/") ? "" : "/",
|
||||
path[0] == '/' ? path+1 : path,
|
||||
rest ? (endswith(path, "/") ? "" : "/") : NULL,
|
||||
rest && rest[0] == '/' ? rest+1 : rest);
|
||||
else
|
||||
return strjoin(path,
|
||||
rest ? (endswith(path, "/") ? "" : "/") : NULL,
|
||||
rest && rest[0] == '/' ? rest+1 : rest);
|
||||
/* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin
|
||||
* already with one so that it is unnecessary. Note that slashes which are already duplicate won't be
|
||||
* removed. The string returned is hence always equal to or longer than the sum of the lengths of each
|
||||
* individual string.
|
||||
*
|
||||
* Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
|
||||
* are optional.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* path_join("foo", "bar") → "foo/bar"
|
||||
* path_join("foo/", "bar") → "foo/bar"
|
||||
* path_join("", "foo", "", "bar", "") → "foo/bar" */
|
||||
|
||||
sz = strlen_ptr(first);
|
||||
va_start(ap, first);
|
||||
while ((p = va_arg(ap, char*)) != (const char*) -1)
|
||||
if (!isempty(p))
|
||||
sz += 1 + strlen(p);
|
||||
va_end(ap);
|
||||
|
||||
joined = new(char, sz + 1);
|
||||
if (!joined)
|
||||
return NULL;
|
||||
|
||||
if (!isempty(first)) {
|
||||
q = stpcpy(joined, first);
|
||||
slash = endswith(first, "/");
|
||||
} else {
|
||||
/* Skip empty items */
|
||||
joined[0] = 0;
|
||||
q = joined;
|
||||
slash = true; /* no need to generate a slash anymore */
|
||||
}
|
||||
|
||||
va_start(ap, first);
|
||||
while ((p = va_arg(ap, char*)) != (const char*) -1) {
|
||||
if (isempty(p))
|
||||
continue;
|
||||
|
||||
if (!slash && p[0] != '/')
|
||||
*(q++) = '/';
|
||||
|
||||
q = stpcpy(q, p);
|
||||
slash = endswith(p, "/");
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return joined;
|
||||
}
|
||||
|
||||
int find_binary(const char *name, char **ret) {
|
||||
|
@ -755,6 +799,9 @@ const char *last_path_component(const char *path) {
|
|||
|
||||
unsigned l, k;
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
l = k = strlen(path);
|
||||
if (l == 0) /* special case — an empty string */
|
||||
return path;
|
||||
|
@ -770,6 +817,37 @@ const char *last_path_component(const char *path) {
|
|||
|
||||
return path + k;
|
||||
}
|
||||
|
||||
int path_extract_filename(const char *p, char **ret) {
|
||||
_cleanup_free_ char *a = NULL;
|
||||
const char *c, *e = NULL, *q;
|
||||
|
||||
/* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
|
||||
* filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */
|
||||
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
c = last_path_component(p);
|
||||
|
||||
for (q = c; *q != 0; q++)
|
||||
if (*q != '/')
|
||||
e = q + 1;
|
||||
|
||||
if (!e) /* no valid character? */
|
||||
return -EINVAL;
|
||||
|
||||
a = strndup(c, e - c);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!filename_is_valid(a))
|
||||
return -EINVAL;
|
||||
|
||||
*ret = TAKE_PTR(a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
bool filename_is_valid(const char *p) {
|
||||
|
@ -926,8 +1004,7 @@ bool valid_device_allow_pattern(const char *path) {
|
|||
/* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny=
|
||||
* accept it */
|
||||
|
||||
if (startswith(path, "block-") ||
|
||||
startswith(path, "char-"))
|
||||
if (STARTSWITH_SET(path, "block-", "char-"))
|
||||
return true;
|
||||
|
||||
return valid_device_node_path(path);
|
||||
|
@ -1071,6 +1148,13 @@ int path_simplify_and_warn(
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!path_is_valid(path)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0,
|
||||
"%s= path has invalid length (%zu bytes)%s.",
|
||||
lvalue, strlen(path), fatal ? "" : ", ignoring");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
|
@ -50,7 +51,9 @@ char* path_startswith(const char *path, const char *prefix) _pure_;
|
|||
int path_compare(const char *a, const char *b) _pure_;
|
||||
bool path_equal(const char *a, const char *b) _pure_;
|
||||
bool path_equal_or_files_same(const char *a, const char *b, int flags);
|
||||
char* path_join(const char *root, const char *path, const char *rest);
|
||||
char* path_join_internal(const char *first, ...);
|
||||
#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, (const char*) -1)
|
||||
|
||||
char* path_simplify(char *path, bool kill_dots);
|
||||
|
||||
static inline bool path_equal_ptr(const char *a, const char *b) {
|
||||
|
@ -72,13 +75,13 @@ static inline bool path_equal_ptr(const char *a, const char *b) {
|
|||
|
||||
#define PATH_STARTSWITH_SET(p, ...) \
|
||||
({ \
|
||||
char **s; \
|
||||
bool _found = false; \
|
||||
STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \
|
||||
if (path_startswith(p, *s)) { \
|
||||
_found = true; \
|
||||
break; \
|
||||
} \
|
||||
const char *_p = (p); \
|
||||
char *_found = NULL, **_i; \
|
||||
STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
|
||||
_found = path_startswith(_p, *_i); \
|
||||
if (_found) \
|
||||
break; \
|
||||
} \
|
||||
_found; \
|
||||
})
|
||||
|
||||
|
@ -96,12 +99,24 @@ int mkfs_exists(const char *fstype);
|
|||
/* Iterates through the path prefixes of the specified path, going up
|
||||
* the tree, to root. Also returns "" (and not "/"!) for the root
|
||||
* directory. Excludes the specified directory itself */
|
||||
#define PATH_FOREACH_PREFIX(prefix, path) \
|
||||
for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
|
||||
#define PATH_FOREACH_PREFIX(prefix, path) \
|
||||
for (char *_slash = ({ \
|
||||
path_simplify(strcpy(prefix, path), false); \
|
||||
streq(prefix, "/") ? NULL : strrchr(prefix, '/'); \
|
||||
}); \
|
||||
_slash && ((*_slash = 0), true); \
|
||||
_slash = strrchr((prefix), '/'))
|
||||
|
||||
/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
|
||||
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
|
||||
for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
|
||||
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
|
||||
for (char *_slash = ({ \
|
||||
path_simplify(strcpy(prefix, path), false); \
|
||||
if (streq(prefix, "/")) \
|
||||
prefix[0] = 0; \
|
||||
strrchr(prefix, 0); \
|
||||
}); \
|
||||
_slash && ((*_slash = 0), true); \
|
||||
_slash = strrchr((prefix), '/'))
|
||||
|
||||
char *prefix_root(const char *root, const char *path);
|
||||
|
||||
|
@ -134,6 +149,7 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar
|
|||
|
||||
char* dirname_malloc(const char *path);
|
||||
const char *last_path_component(const char *path);
|
||||
int path_extract_filename(const char *p, char **ret);
|
||||
|
||||
bool filename_is_valid(const char *p) _pure_;
|
||||
bool path_is_valid(const char *p) _pure_;
|
||||
|
|
|
@ -67,9 +67,6 @@ int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
|
|||
}
|
||||
|
||||
static void swap(Prioq *q, unsigned j, unsigned k) {
|
||||
void *saved_data;
|
||||
unsigned *saved_idx;
|
||||
|
||||
assert(q);
|
||||
assert(j < q->n_items);
|
||||
assert(k < q->n_items);
|
||||
|
@ -77,12 +74,8 @@ static void swap(Prioq *q, unsigned j, unsigned k) {
|
|||
assert(!q->items[j].idx || *(q->items[j].idx) == j);
|
||||
assert(!q->items[k].idx || *(q->items[k].idx) == k);
|
||||
|
||||
saved_data = q->items[j].data;
|
||||
saved_idx = q->items[j].idx;
|
||||
q->items[j].data = q->items[k].data;
|
||||
q->items[j].idx = q->items[k].idx;
|
||||
q->items[k].data = saved_data;
|
||||
q->items[k].idx = saved_idx;
|
||||
SWAP_TWO(q->items[j].data, q->items[k].data);
|
||||
SWAP_TWO(q->items[j].idx, q->items[k].idx);
|
||||
|
||||
if (q->items[j].idx)
|
||||
*q->items[j].idx = j;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "missing.h"
|
||||
#include "process-util.h"
|
||||
#include "raw-clone.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
|
@ -604,12 +605,14 @@ int get_process_root(pid_t pid, char **root) {
|
|||
return get_process_link_contents(p, root);
|
||||
}
|
||||
|
||||
#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)
|
||||
|
||||
int get_process_environ(pid_t pid, char **env) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *outcome = NULL;
|
||||
int c;
|
||||
const char *p;
|
||||
size_t allocated = 0, sz = 0;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(env);
|
||||
|
@ -625,23 +628,28 @@ int get_process_environ(pid_t pid, char **env) {
|
|||
|
||||
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
|
||||
|
||||
while ((c = fgetc(f)) != EOF) {
|
||||
for (;;) {
|
||||
char c;
|
||||
|
||||
if (sz >= ENVIRONMENT_BLOCK_MAX)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
|
||||
return -ENOMEM;
|
||||
|
||||
r = safe_fgetc(f, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (c == '\0')
|
||||
outcome[sz++] = '\n';
|
||||
else
|
||||
sz += cescape_char(c, outcome + sz);
|
||||
}
|
||||
|
||||
if (!outcome) {
|
||||
outcome = strdup("");
|
||||
if (!outcome)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
outcome[sz] = '\0';
|
||||
|
||||
outcome[sz] = '\0';
|
||||
*env = TAKE_PTR(outcome);
|
||||
|
||||
return 0;
|
||||
|
@ -874,9 +882,9 @@ int kill_and_sigcont(pid_t pid, int sig) {
|
|||
int getenv_for_pid(pid_t pid, const char *field, char **ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
char *value = NULL;
|
||||
bool done = false;
|
||||
const char *path;
|
||||
size_t l;
|
||||
size_t l, sum = 0;
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(field);
|
||||
|
@ -899,6 +907,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (!pid_is_valid(pid))
|
||||
return -EINVAL;
|
||||
|
||||
path = procfs_file_alloca(pid, "environ");
|
||||
|
||||
f = fopen(path, "re");
|
||||
|
@ -912,24 +923,19 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
|
|||
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
|
||||
|
||||
l = strlen(field);
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
|
||||
do {
|
||||
char line[LINE_MAX];
|
||||
size_t i;
|
||||
if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */
|
||||
return -ENOBUFS;
|
||||
|
||||
for (i = 0; i < sizeof(line)-1; i++) {
|
||||
int c;
|
||||
r = read_nul_string(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* EOF */
|
||||
break;
|
||||
|
||||
c = getc(f);
|
||||
if (_unlikely_(c == EOF)) {
|
||||
done = true;
|
||||
break;
|
||||
} else if (c == 0)
|
||||
break;
|
||||
|
||||
line[i] = c;
|
||||
}
|
||||
line[i] = 0;
|
||||
sum += r;
|
||||
|
||||
if (strneq(line, field, l) && line[l] == '=') {
|
||||
value = strdup(line + l + 1);
|
||||
|
@ -939,8 +945,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
|
|||
*ret = value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
} while (!done);
|
||||
}
|
||||
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
|
@ -1180,7 +1185,7 @@ void reset_cached_pid(void) {
|
|||
* headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against
|
||||
* libpthread, as it is part of glibc anyway. */
|
||||
extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle);
|
||||
extern void* __dso_handle __attribute__ ((__weak__));
|
||||
extern void* __dso_handle _weak_;
|
||||
|
||||
pid_t getpid_cached(void) {
|
||||
static bool installed = false;
|
||||
|
@ -1259,25 +1264,17 @@ int safe_fork_full(
|
|||
original_pid = getpid_cached();
|
||||
|
||||
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
|
||||
|
||||
/* 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_full_errno(prio, errno, "Failed to reset signal set: %m");
|
||||
|
||||
assert_se(sigfillset(&ss) >= 0);
|
||||
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");
|
||||
|
||||
assert_se(sigemptyset(&ss) >= 0);
|
||||
assert_se(sigaddset(&ss, SIGCHLD) >= 0);
|
||||
block_signals = true;
|
||||
}
|
||||
|
||||
|
@ -1410,6 +1407,14 @@ int safe_fork_full(
|
|||
}
|
||||
}
|
||||
|
||||
if (flags & FORK_RLIMIT_NOFILE_SAFE) {
|
||||
r = rlimit_nofile_safe();
|
||||
if (r < 0) {
|
||||
log_full_errno(prio, r, "Failed to lower RLIMIT_NOFILE's soft limit to 1K: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret_pid)
|
||||
*ret_pid = getpid_cached();
|
||||
|
||||
|
@ -1521,6 +1526,8 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret
|
|||
safe_close_above_stdio(fd);
|
||||
}
|
||||
|
||||
(void) rlimit_nofile_safe();
|
||||
|
||||
/* Count arguments */
|
||||
va_start(ap, path);
|
||||
for (n = 0; va_arg(ap, char*); n++)
|
||||
|
|
|
@ -144,15 +144,16 @@ void reset_cached_pid(void);
|
|||
int must_be_root(void);
|
||||
|
||||
typedef enum ForkFlags {
|
||||
FORK_RESET_SIGNALS = 1 << 0,
|
||||
FORK_CLOSE_ALL_FDS = 1 << 1,
|
||||
FORK_DEATHSIG = 1 << 2,
|
||||
FORK_NULL_STDIO = 1 << 3,
|
||||
FORK_REOPEN_LOG = 1 << 4,
|
||||
FORK_LOG = 1 << 5,
|
||||
FORK_WAIT = 1 << 6,
|
||||
FORK_NEW_MOUNTNS = 1 << 7,
|
||||
FORK_MOUNTNS_SLAVE = 1 << 8,
|
||||
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
|
||||
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
|
||||
FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */
|
||||
FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */
|
||||
FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */
|
||||
FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */
|
||||
FORK_WAIT = 1 << 6, /* Wait until child exited */
|
||||
FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */
|
||||
FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */
|
||||
FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
|
||||
} ForkFlags;
|
||||
|
||||
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/random.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -9,13 +9,11 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
|
|||
#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
|
||||
|
||||
static inline Set *set_free(Set *s) {
|
||||
internal_hashmap_free(HASHMAP_BASE(s));
|
||||
return NULL;
|
||||
return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL);
|
||||
}
|
||||
|
||||
static inline Set *set_free_free(Set *s) {
|
||||
internal_hashmap_free_free(HASHMAP_BASE(s));
|
||||
return NULL;
|
||||
return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL);
|
||||
}
|
||||
|
||||
/* no set_free_free_free */
|
||||
|
@ -76,11 +74,11 @@ static inline unsigned set_buckets(Set *s) {
|
|||
bool set_iterate(Set *s, Iterator *i, void **value);
|
||||
|
||||
static inline void set_clear(Set *s) {
|
||||
internal_hashmap_clear(HASHMAP_BASE(s));
|
||||
internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
|
||||
}
|
||||
|
||||
static inline void set_clear_free(Set *s) {
|
||||
internal_hashmap_clear_free(HASHMAP_BASE(s));
|
||||
internal_hashmap_clear(HASHMAP_BASE(s), free, NULL);
|
||||
}
|
||||
|
||||
/* no set_clear_free_free */
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
|
@ -114,7 +115,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
|
|||
size_t l;
|
||||
|
||||
l = strlen(s+1);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminate sockets here
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated sockets here
|
||||
* when parsing, even though abstract namespace sockets
|
||||
* explicitly allow embedded NUL bytes and don't consider
|
||||
* them special. But it's simply annoying to debug such
|
||||
|
@ -264,9 +265,12 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_verify(const SocketAddress *a) {
|
||||
int socket_address_verify(const SocketAddress *a, bool strict) {
|
||||
assert(a);
|
||||
|
||||
/* With 'strict' we enforce additional sanity constraints which are not set by the standard,
|
||||
* but should only apply to sockets we create ourselves. */
|
||||
|
||||
switch (socket_address_family(a)) {
|
||||
|
||||
case AF_INET:
|
||||
|
@ -296,19 +300,20 @@ int socket_address_verify(const SocketAddress *a) {
|
|||
case AF_UNIX:
|
||||
if (a->size < offsetof(struct sockaddr_un, sun_path))
|
||||
return -EINVAL;
|
||||
if (a->size > sizeof(struct sockaddr_un)+1) /* Allow one extra byte, since getsockname() on Linux will
|
||||
* append a NUL byte if we have path sockets that are above
|
||||
* sun_path' full size */
|
||||
if (a->size > sizeof(struct sockaddr_un) + !strict)
|
||||
/* If !strict, allow one extra byte, since getsockname() on Linux will append
|
||||
* a NUL byte if we have path sockets that are above sun_path's full size. */
|
||||
return -EINVAL;
|
||||
|
||||
if (a->size > offsetof(struct sockaddr_un, sun_path) &&
|
||||
a->sockaddr.un.sun_path[0] != 0) { /* Only validate file system sockets here */
|
||||
|
||||
a->sockaddr.un.sun_path[0] != 0 &&
|
||||
strict) {
|
||||
/* Only validate file system sockets here, and only in strict mode */
|
||||
const char *e;
|
||||
|
||||
e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
|
||||
if (e) {
|
||||
/* If there's an embedded NUL byte, make sure the size of the socket addresses matches it */
|
||||
/* If there's an embedded NUL byte, make sure the size of the socket address matches it */
|
||||
if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
|
@ -356,7 +361,10 @@ int socket_address_print(const SocketAddress *a, char **ret) {
|
|||
assert(a);
|
||||
assert(ret);
|
||||
|
||||
r = socket_address_verify(a);
|
||||
r = socket_address_verify(a, false); /* We do non-strict validation, because we want to be
|
||||
* able to pretty-print any socket the kernel considers
|
||||
* valid. We still need to do validation to know if we
|
||||
* can meaningfully print the address. */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -389,8 +397,8 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
|
|||
assert(b);
|
||||
|
||||
/* Invalid addresses are unequal to all */
|
||||
if (socket_address_verify(a) < 0 ||
|
||||
socket_address_verify(b) < 0)
|
||||
if (socket_address_verify(a, false) < 0 ||
|
||||
socket_address_verify(b, false) < 0)
|
||||
return false;
|
||||
|
||||
if (a->type != b->type)
|
||||
|
@ -574,7 +582,13 @@ int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) {
|
|||
}
|
||||
}
|
||||
|
||||
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
|
||||
int sockaddr_pretty(
|
||||
const struct sockaddr *_sa,
|
||||
socklen_t salen,
|
||||
bool translate_ipv6,
|
||||
bool include_port,
|
||||
char **ret) {
|
||||
|
||||
union sockaddr_union *sa = (union sockaddr_union*) _sa;
|
||||
char *p;
|
||||
int r;
|
||||
|
@ -645,42 +659,51 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
|
|||
}
|
||||
|
||||
case AF_UNIX:
|
||||
if (salen <= offsetof(struct sockaddr_un, sun_path)) {
|
||||
if (salen <= offsetof(struct sockaddr_un, sun_path) ||
|
||||
(sa->un.sun_path[0] == 0 && salen == offsetof(struct sockaddr_un, sun_path) + 1))
|
||||
/* The name must have at least one character (and the leading NUL does not count) */
|
||||
p = strdup("<unnamed>");
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
else {
|
||||
/* Note that we calculate the path pointer here through the .un_buffer[] field, in order to
|
||||
* outtrick bounds checking tools such as ubsan, which are too smart for their own good: on
|
||||
* Linux the kernel may return sun_path[] data one byte longer than the declared size of the
|
||||
* field. */
|
||||
char *path = (char*) sa->un_buffer + offsetof(struct sockaddr_un, sun_path);
|
||||
size_t path_len = salen - offsetof(struct sockaddr_un, sun_path);
|
||||
|
||||
} else if (sa->un.sun_path[0] == 0) {
|
||||
/* abstract */
|
||||
if (path[0] == 0) {
|
||||
/* Abstract socket. When parsing address information from, we
|
||||
* explicitly reject overly long paths and paths with embedded NULs.
|
||||
* But we might get such a socket from the outside. Let's return
|
||||
* something meaningful and printable in this case. */
|
||||
|
||||
/* FIXME: We assume we can print the
|
||||
* socket path here and that it hasn't
|
||||
* more than one NUL byte. That is
|
||||
* actually an invalid assumption */
|
||||
_cleanup_free_ char *e = NULL;
|
||||
|
||||
p = new(char, sizeof(sa->un.sun_path)+1);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
e = cescape_length(path + 1, path_len - 1);
|
||||
if (!e)
|
||||
return -ENOMEM;
|
||||
|
||||
p[0] = '@';
|
||||
memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
|
||||
p[sizeof(sa->un.sun_path)] = 0;
|
||||
p = strjoin("@", e);
|
||||
} else {
|
||||
if (path[path_len - 1] == '\0')
|
||||
/* We expect a terminating NUL and don't print it */
|
||||
path_len --;
|
||||
|
||||
} else {
|
||||
p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
p = cescape_length(path, path_len);
|
||||
}
|
||||
}
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
break;
|
||||
|
||||
case AF_VSOCK:
|
||||
if (include_port)
|
||||
r = asprintf(&p,
|
||||
"vsock:%u:%u",
|
||||
sa->vm.svm_cid,
|
||||
sa->vm.svm_port);
|
||||
else
|
||||
if (include_port) {
|
||||
if (sa->vm.svm_cid == VMADDR_CID_ANY)
|
||||
r = asprintf(&p, "vsock::%u", sa->vm.svm_port);
|
||||
else
|
||||
r = asprintf(&p, "vsock:%u:%u", sa->vm.svm_cid, sa->vm.svm_port);
|
||||
} else
|
||||
r = asprintf(&p, "vsock:%u", sa->vm.svm_cid);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/if_infiniband.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
|
@ -8,13 +12,10 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/if_infiniband.h>
|
||||
#include <linux/if_packet.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "missing.h"
|
||||
#include "util.h"
|
||||
#include "missing_socket.h"
|
||||
#include "sparse-endian.h"
|
||||
|
||||
union sockaddr_union {
|
||||
/* The minimal, abstract version */
|
||||
|
@ -72,7 +73,7 @@ int socket_address_parse(SocketAddress *a, const char *s);
|
|||
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s);
|
||||
int socket_address_print(const SocketAddress *a, char **p);
|
||||
int socket_address_verify(const SocketAddress *a) _pure_;
|
||||
int socket_address_verify(const SocketAddress *a, bool strict) _pure_;
|
||||
|
||||
int sockaddr_un_unlink(const struct sockaddr_un *sa);
|
||||
|
||||
|
|
|
@ -12,11 +12,13 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
#include "missing.h"
|
||||
#include "parse-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
|
@ -300,3 +302,124 @@ int fd_verify_regular(int fd) {
|
|||
|
||||
return stat_verify_regular(&st);
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int stat_verify_directory(const struct stat *st) {
|
||||
assert(st);
|
||||
|
||||
if (S_ISLNK(st->st_mode))
|
||||
return -ELOOP;
|
||||
|
||||
if (!S_ISDIR(st->st_mode))
|
||||
return -ENOTDIR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_verify_directory(int fd) {
|
||||
struct stat st;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
return stat_verify_directory(&st);
|
||||
}
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) {
|
||||
const char *t;
|
||||
|
||||
/* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
|
||||
|
||||
if (S_ISCHR(mode))
|
||||
t = "char";
|
||||
else if (S_ISBLK(mode))
|
||||
t = "block";
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
/* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (major(devno) == 0 && minor(devno) == 0) {
|
||||
char *s;
|
||||
|
||||
/* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
|
||||
* /dev/block/ and /dev/char/, hence we handle them specially here. */
|
||||
|
||||
if (S_ISCHR(mode))
|
||||
s = strdup("/run/systemd/inaccessible/chr");
|
||||
else if (S_ISBLK(mode))
|
||||
s = strdup("/run/systemd/inaccessible/blk");
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = device_path_make_major_minor(mode, devno, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return chase_symlinks(p, NULL, 0, ret);
|
||||
}
|
||||
|
||||
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) {
|
||||
mode_t mode;
|
||||
dev_t devno;
|
||||
int r;
|
||||
|
||||
/* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
|
||||
* paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
|
||||
* path cannot be parsed like this. */
|
||||
|
||||
if (path_equal(path, "/run/systemd/inaccessible/chr")) {
|
||||
mode = S_IFCHR;
|
||||
devno = makedev(0, 0);
|
||||
} else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
|
||||
mode = S_IFBLK;
|
||||
devno = makedev(0, 0);
|
||||
} else {
|
||||
const char *w;
|
||||
|
||||
w = path_startswith(path, "/dev/block/");
|
||||
if (w)
|
||||
mode = S_IFBLK;
|
||||
else {
|
||||
w = path_startswith(path, "/dev/char/");
|
||||
if (!w)
|
||||
return -ENODEV;
|
||||
|
||||
mode = S_IFCHR;
|
||||
}
|
||||
|
||||
r = parse_dev(w, &devno);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_mode)
|
||||
*ret_mode = mode;
|
||||
if (ret_devno)
|
||||
*ret_devno = devno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
@ -59,3 +59,29 @@ int path_is_temporary_fs(const char *path);
|
|||
|
||||
int stat_verify_regular(const struct stat *st);
|
||||
int fd_verify_regular(int fd);
|
||||
|
||||
int stat_verify_directory(const struct stat *st);
|
||||
int fd_verify_directory(int fd);
|
||||
|
||||
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
|
||||
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
|
||||
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
|
||||
* comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
|
||||
* such a test would be pointless in such a case.) */
|
||||
|
||||
#define DEVICE_MAJOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 12); \
|
||||
\
|
||||
})
|
||||
|
||||
#define DEVICE_MINOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 20); \
|
||||
})
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
|
||||
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
|
||||
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);
|
||||
|
|
|
@ -144,6 +144,18 @@ void strv_print(char **l);
|
|||
_x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \
|
||||
})
|
||||
|
||||
#define STARTSWITH_SET(p, ...) \
|
||||
({ \
|
||||
const char *_p = (p); \
|
||||
char *_found = NULL, **_i; \
|
||||
STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
|
||||
_found = startswith(_p, *_i); \
|
||||
if (_found) \
|
||||
break; \
|
||||
} \
|
||||
_found; \
|
||||
})
|
||||
|
||||
#define FOREACH_STRING(x, ...) \
|
||||
for (char **_l = ({ \
|
||||
char **_ll = STRV_MAKE(__VA_ARGS__); \
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing_timerfd.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
|
@ -1391,9 +1392,7 @@ int get_timezone(char **tz) {
|
|||
if (r < 0)
|
||||
return r; /* returns EINVAL if not a symlink */
|
||||
|
||||
e = path_startswith(t, "/usr/share/zoneinfo/");
|
||||
if (!e)
|
||||
e = path_startswith(t, "../usr/share/zoneinfo/");
|
||||
e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
335
src/systemd/src/basic/tmpfile-util.c
Normal file
335
src/systemd/src/basic/tmpfile-util.c
Normal file
|
@ -0,0 +1,335 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include "nm-sd-adapt.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "macro.h"
|
||||
#include "memfd-util.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "random-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "umask-util.h"
|
||||
|
||||
int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
|
||||
FILE *f;
|
||||
char *t;
|
||||
int r, fd;
|
||||
|
||||
assert(path);
|
||||
assert(_f);
|
||||
assert(_temp_path);
|
||||
|
||||
r = tempfn_xxxxxx(path, NULL, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = mkostemp_safe(t);
|
||||
if (fd < 0) {
|
||||
free(t);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
f = fdopen(fd, "w");
|
||||
if (!f) {
|
||||
unlink_noerrno(t);
|
||||
free(t);
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*_f = f;
|
||||
*_temp_path = t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is much like mkostemp() but is subject to umask(). */
|
||||
int mkostemp_safe(char *pattern) {
|
||||
_cleanup_umask_ mode_t u = 0;
|
||||
int fd;
|
||||
|
||||
assert(pattern);
|
||||
|
||||
u = umask(077);
|
||||
|
||||
fd = mkostemp(pattern, O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
|
||||
int fd;
|
||||
FILE *f;
|
||||
|
||||
fd = mkostemp_safe(pattern);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
f = fdopen(fd, mode);
|
||||
if (!f) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*ret_f = f;
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
|
||||
const char *fn;
|
||||
char *t;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (isempty(p))
|
||||
return -EINVAL;
|
||||
if (path_equal(p, "/"))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Turns this:
|
||||
* /foo/bar/waldo
|
||||
*
|
||||
* Into this:
|
||||
* /foo/bar/.#<extra>waldoXXXXXX
|
||||
*/
|
||||
|
||||
fn = basename(p);
|
||||
if (!filename_is_valid(fn))
|
||||
return -EINVAL;
|
||||
|
||||
extra = strempty(extra);
|
||||
|
||||
t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
|
||||
|
||||
*ret = path_simplify(t, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int tempfn_random(const char *p, const char *extra, char **ret) {
|
||||
const char *fn;
|
||||
char *t, *x;
|
||||
uint64_t u;
|
||||
unsigned i;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (isempty(p))
|
||||
return -EINVAL;
|
||||
if (path_equal(p, "/"))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Turns this:
|
||||
* /foo/bar/waldo
|
||||
*
|
||||
* Into this:
|
||||
* /foo/bar/.#<extra>waldobaa2a261115984a9
|
||||
*/
|
||||
|
||||
fn = basename(p);
|
||||
if (!filename_is_valid(fn))
|
||||
return -EINVAL;
|
||||
|
||||
extra = strempty(extra);
|
||||
|
||||
t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
|
||||
|
||||
u = random_u64();
|
||||
for (i = 0; i < 16; i++) {
|
||||
*(x++) = hexchar(u & 0xF);
|
||||
u >>= 4;
|
||||
}
|
||||
|
||||
*x = 0;
|
||||
|
||||
*ret = path_simplify(t, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tempfn_random_child(const char *p, const char *extra, char **ret) {
|
||||
char *t, *x;
|
||||
uint64_t u;
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
/* Turns this:
|
||||
* /foo/bar/waldo
|
||||
* Into this:
|
||||
* /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
|
||||
*/
|
||||
|
||||
if (!p) {
|
||||
r = tmp_dir(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
extra = strempty(extra);
|
||||
|
||||
t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
if (isempty(p))
|
||||
x = stpcpy(stpcpy(t, ".#"), extra);
|
||||
else
|
||||
x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
|
||||
|
||||
u = random_u64();
|
||||
for (i = 0; i < 16; i++) {
|
||||
*(x++) = hexchar(u & 0xF);
|
||||
u >>= 4;
|
||||
}
|
||||
|
||||
*x = 0;
|
||||
|
||||
*ret = path_simplify(t, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_tmpfile_unlinkable(const char *directory, int flags) {
|
||||
char *p;
|
||||
int fd, r;
|
||||
|
||||
if (!directory) {
|
||||
r = tmp_dir(&directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (isempty(directory))
|
||||
return -EINVAL;
|
||||
|
||||
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */
|
||||
|
||||
/* Try O_TMPFILE first, if it is supported */
|
||||
fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
|
||||
/* Fall back to unguessable name + unlinking */
|
||||
p = strjoina(directory, "/systemd-tmp-XXXXXX");
|
||||
|
||||
fd = mkostemp_safe(p);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
(void) unlink(p);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
|
||||
_cleanup_free_ char *tmp = NULL;
|
||||
int r, fd;
|
||||
|
||||
assert(target);
|
||||
assert(ret_path);
|
||||
|
||||
/* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
|
||||
assert((flags & O_EXCL) == 0);
|
||||
|
||||
/* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
|
||||
* which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
|
||||
* "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
|
||||
|
||||
fd = open_parent(target, O_TMPFILE|flags, 0640);
|
||||
if (fd >= 0) {
|
||||
*ret_path = NULL;
|
||||
return fd;
|
||||
}
|
||||
|
||||
log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target);
|
||||
|
||||
r = tempfn_random(target, NULL, &tmp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
*ret_path = TAKE_PTR(tmp);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int link_tmpfile(int fd, const char *path, const char *target) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(target);
|
||||
|
||||
/* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
|
||||
* created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
|
||||
* on the directory, and renameat2() is used instead.
|
||||
*
|
||||
* Note that in both cases we will not replace existing files. This is because linkat() does not support this
|
||||
* operation currently (renameat2() does), and there is no nice way to emulate this. */
|
||||
|
||||
if (path) {
|
||||
r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
|
||||
|
||||
xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
|
||||
|
||||
if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mkdtemp_malloc(const char *template, char **ret) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (template)
|
||||
p = strdup(template);
|
||||
else {
|
||||
const char *tmp;
|
||||
|
||||
r = tmp_dir(&tmp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = strjoin(tmp, "/XXXXXX");
|
||||
}
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!mkdtemp(p))
|
||||
return -errno;
|
||||
|
||||
*ret = TAKE_PTR(p);
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
19
src/systemd/src/basic/tmpfile-util.h
Normal file
19
src/systemd/src/basic/tmpfile-util.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
|
||||
int mkostemp_safe(char *pattern);
|
||||
int fmkostemp_safe(char *pattern, const char *mode, FILE**_f);
|
||||
|
||||
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
|
||||
int tempfn_random(const char *p, const char *extra, char **ret);
|
||||
int tempfn_random_child(const char *p, const char *extra, char **ret);
|
||||
|
||||
int open_tmpfile_unlinkable(const char *directory, int flags);
|
||||
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
|
||||
|
||||
int link_tmpfile(int fd, const char *path, const char *target);
|
||||
|
||||
int mkdtemp_malloc(const char *template, char **ret);
|
|
@ -9,7 +9,7 @@
|
|||
#endif /* NM_IGNORED */
|
||||
|
||||
#include "macro.h"
|
||||
#include "missing.h"
|
||||
#include "missing_type.h"
|
||||
|
||||
#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
|
||||
#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf"
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "def.h"
|
||||
#include "device-nodes.h"
|
||||
#include "dirent-util.h"
|
||||
#include "env-file.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include "format-util.h"
|
||||
#include "macro.h"
|
||||
#include "missing.h"
|
||||
#include "time-util.h"
|
||||
|
||||
size_t page_size(void) _pure_;
|
||||
|
@ -148,6 +147,12 @@ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
|
|||
return memcmp(s1, s2, n);
|
||||
}
|
||||
|
||||
/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */
|
||||
static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) {
|
||||
return memcmp_safe(s1, s2, MIN(n1, n2))
|
||||
?: CMP(n1, n2);
|
||||
}
|
||||
|
||||
int on_ac_power(void);
|
||||
|
||||
#define memzero(x,l) \
|
||||
|
@ -173,7 +178,7 @@ static inline void _reset_errno_(int *saved_errno) {
|
|||
}
|
||||
|
||||
#define PROTECT_ERRNO \
|
||||
_cleanup_(_reset_errno_) __attribute__((__unused__)) int _saved_errno_ = errno
|
||||
_cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno
|
||||
|
||||
static inline int negative_errno(void) {
|
||||
/* This helper should be used to shut up gcc if you know 'errno' is
|
||||
|
@ -194,7 +199,7 @@ static inline unsigned u64log2(uint64_t n) {
|
|||
|
||||
static inline unsigned u32ctz(uint32_t n) {
|
||||
#if __SIZEOF_INT__ == 4
|
||||
return __builtin_ctz(n);
|
||||
return n != 0 ? __builtin_ctz(n) : 32;
|
||||
#else
|
||||
#error "Wut?"
|
||||
#endif
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
#include "in-addr-util.h"
|
||||
#include "lldp-internal.h"
|
||||
#include "lldp-neighbor.h"
|
||||
#include "missing.h"
|
||||
#include "unaligned.h"
|
||||
#include "util.h"
|
||||
|
||||
static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) {
|
||||
const LLDPNeighborID *id = p;
|
||||
|
||||
static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
|
||||
siphash24_compress(id->chassis_id, id->chassis_id_size, state);
|
||||
siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
|
||||
siphash24_compress(id->port_id, id->port_id_size, state);
|
||||
|
@ -21,27 +21,12 @@ static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) {
|
|||
}
|
||||
|
||||
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
|
||||
int r;
|
||||
|
||||
r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size));
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = CMP(x->chassis_id_size, y->chassis_id_size);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size));
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return CMP(x->port_id_size, y->port_id_size);
|
||||
return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
|
||||
?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
|
||||
}
|
||||
|
||||
const struct hash_ops lldp_neighbor_id_hash_ops = {
|
||||
.hash = lldp_neighbor_id_hash_func,
|
||||
.compare = (__compar_fn_t) lldp_neighbor_id_compare_func,
|
||||
};
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
|
||||
sd_lldp_neighbor, lldp_neighbor_unlink);
|
||||
|
||||
int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
|
||||
const sd_lldp_neighbor *x = a, *y = b;
|
||||
|
@ -99,7 +84,12 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
|
|||
if (!n->lldp)
|
||||
return NULL;
|
||||
|
||||
assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n);
|
||||
/* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
|
||||
* because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
|
||||
* ourselves from the hashtable and sometimes are called after we already are de-registered. */
|
||||
|
||||
(void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
|
||||
|
||||
assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
|
||||
|
||||
n->lldp = NULL;
|
||||
|
|
|
@ -80,7 +80,7 @@ static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
|
|||
return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
|
||||
}
|
||||
|
||||
extern const struct hash_ops lldp_neighbor_id_hash_ops;
|
||||
extern const struct hash_ops lldp_neighbor_hash_ops;
|
||||
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y);
|
||||
int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "fd-util.h"
|
||||
#include "lldp-network.h"
|
||||
#include "missing.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
int lldp_network_bind_raw_socket(int ifindex) {
|
||||
|
|
|
@ -374,36 +374,6 @@ int config_parse_hwaddrs(const char *unit,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_iaid(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) {
|
||||
uint32_t iaid;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = safe_atou32(rvalue, &iaid);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
||||
"Unable to read IAID, ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*((uint32_t *)data) = iaid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_bridge_port_priority(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
|
|
@ -36,7 +36,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
|
|||
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_ifalias);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority);
|
||||
|
||||
int net_get_unique_predictable_data(sd_device *device, uint64_t *result);
|
||||
|
|
|
@ -25,10 +25,11 @@
|
|||
#include "dns-domain.h"
|
||||
#include "event-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "io-util.h"
|
||||
#include "random-util.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
#include "strv.h"
|
||||
#include "util.h"
|
||||
|
||||
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
|
||||
#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
|
||||
|
@ -343,8 +344,9 @@ int sd_dhcp_client_set_client_id(
|
|||
*/
|
||||
static int dhcp_client_set_iaid_duid_internal(
|
||||
sd_dhcp_client *client,
|
||||
bool iaid_append,
|
||||
bool iaid_set,
|
||||
uint32_t iaid,
|
||||
bool append_iaid,
|
||||
uint16_t duid_type,
|
||||
const void *duid,
|
||||
size_t duid_len,
|
||||
|
@ -366,17 +368,17 @@ static int dhcp_client_set_iaid_duid_internal(
|
|||
zero(client->client_id);
|
||||
client->client_id.type = 255;
|
||||
|
||||
if (append_iaid) {
|
||||
/* If IAID is not configured, generate it. */
|
||||
if (iaid == 0) {
|
||||
if (iaid_append) {
|
||||
if (iaid_set)
|
||||
client->client_id.ns.iaid = htobe32(iaid);
|
||||
else {
|
||||
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
|
||||
client->mac_addr_len,
|
||||
true,
|
||||
&client->client_id.ns.iaid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
client->client_id.ns.iaid = htobe32(iaid);
|
||||
}
|
||||
}
|
||||
|
||||
if (duid != NULL) {
|
||||
|
@ -416,10 +418,10 @@ static int dhcp_client_set_iaid_duid_internal(
|
|||
}
|
||||
|
||||
client->client_id_len = sizeof(client->client_id.type) + len +
|
||||
(append_iaid ? sizeof(client->client_id.ns.iaid) : 0);
|
||||
(iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
|
||||
|
||||
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
|
||||
log_dhcp_client(client, "Configured %sDUID, restarting.", append_iaid ? "IAID+" : "");
|
||||
log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
|
||||
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
|
||||
sd_dhcp_client_start(client);
|
||||
}
|
||||
|
@ -429,18 +431,20 @@ static int dhcp_client_set_iaid_duid_internal(
|
|||
|
||||
int sd_dhcp_client_set_iaid_duid(
|
||||
sd_dhcp_client *client,
|
||||
bool iaid_set,
|
||||
uint32_t iaid,
|
||||
uint16_t duid_type,
|
||||
const void *duid,
|
||||
size_t duid_len) {
|
||||
return dhcp_client_set_iaid_duid_internal(client, iaid, true, duid_type, duid, duid_len, 0);
|
||||
return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0);
|
||||
}
|
||||
|
||||
int sd_dhcp_client_set_iaid_duid_llt(
|
||||
sd_dhcp_client *client,
|
||||
bool iaid_set,
|
||||
uint32_t iaid,
|
||||
usec_t llt_time) {
|
||||
return dhcp_client_set_iaid_duid_internal(client, iaid, true, DUID_TYPE_LLT, NULL, 0, llt_time);
|
||||
return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
|
||||
}
|
||||
|
||||
int sd_dhcp_client_set_duid(
|
||||
|
@ -448,13 +452,13 @@ int sd_dhcp_client_set_duid(
|
|||
uint16_t duid_type,
|
||||
const void *duid,
|
||||
size_t duid_len) {
|
||||
return dhcp_client_set_iaid_duid_internal(client, 0, false, duid_type, duid, duid_len, 0);
|
||||
return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0);
|
||||
}
|
||||
|
||||
int sd_dhcp_client_set_duid_llt(
|
||||
sd_dhcp_client *client,
|
||||
usec_t llt_time) {
|
||||
return dhcp_client_set_iaid_duid_internal(client, 0, false, DUID_TYPE_LLT, NULL, 0, llt_time);
|
||||
return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
|
@ -1784,8 +1788,7 @@ static int client_receive_message_raw(
|
|||
if (!packet)
|
||||
return -ENOMEM;
|
||||
|
||||
iov.iov_base = packet;
|
||||
iov.iov_len = buflen;
|
||||
iov = IOVEC_MAKE(packet, buflen);
|
||||
|
||||
len = recvmsg(fd, &msg, 0);
|
||||
if (len < 0) {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "dhcp-lease-internal.h"
|
||||
#include "dhcp-protocol.h"
|
||||
#include "dns-domain.h"
|
||||
#include "env-file.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hexdecoct.h"
|
||||
|
@ -28,6 +29,7 @@
|
|||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
|
||||
|
@ -355,7 +357,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
r = dns_name_normalize(name, &normalized);
|
||||
r = dns_name_normalize(name, 0, &normalized);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ struct sd_dhcp6_client {
|
|||
struct sd_dhcp6_lease *lease;
|
||||
int fd;
|
||||
bool information_request;
|
||||
bool has_iaid;
|
||||
bool iaid_set;
|
||||
be16_t *req_opts;
|
||||
size_t req_opts_allocated;
|
||||
size_t req_opts_len;
|
||||
|
@ -278,7 +278,7 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
|
|||
|
||||
client->ia_na.ia_na.id = htobe32(iaid);
|
||||
client->ia_pd.ia_pd.id = htobe32(iaid);
|
||||
client->has_iaid = true;
|
||||
client->iaid_set = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -802,7 +802,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
|
|||
|
||||
assert(client);
|
||||
|
||||
if (client->has_iaid)
|
||||
if (client->iaid_set)
|
||||
return 0;
|
||||
|
||||
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid);
|
||||
|
@ -811,7 +811,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
|
|||
|
||||
client->ia_na.ia_na.id = iaid;
|
||||
client->ia_pd.ia_pd.id = iaid;
|
||||
client->has_iaid = true;
|
||||
client->iaid_set = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "sd-lldp.h"
|
||||
|
||||
|
@ -29,12 +30,9 @@ static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
|
|||
DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
|
||||
|
||||
static void lldp_flush_neighbors(sd_lldp *lldp) {
|
||||
sd_lldp_neighbor *n;
|
||||
|
||||
assert(lldp);
|
||||
|
||||
while ((n = hashmap_first(lldp->neighbor_by_id)))
|
||||
lldp_neighbor_unlink(n);
|
||||
hashmap_clear(lldp->neighbor_by_id);
|
||||
}
|
||||
|
||||
static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
|
||||
|
@ -377,7 +375,7 @@ _public_ int sd_lldp_new(sd_lldp **ret) {
|
|||
.capability_mask = (uint16_t) -1,
|
||||
};
|
||||
|
||||
lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops);
|
||||
lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
|
||||
if (!lldp->neighbor_by_id)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -1375,8 +1375,7 @@ static int event_make_inotify_data(
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int inode_data_compare(const void *a, const void *b) {
|
||||
const struct inode_data *x = a, *y = b;
|
||||
static int inode_data_compare(const struct inode_data *x, const struct inode_data *y) {
|
||||
int r;
|
||||
|
||||
assert(x);
|
||||
|
@ -1389,19 +1388,14 @@ static int inode_data_compare(const void *a, const void *b) {
|
|||
return CMP(x->ino, y->ino);
|
||||
}
|
||||
|
||||
static void inode_data_hash_func(const void *p, struct siphash *state) {
|
||||
const struct inode_data *d = p;
|
||||
|
||||
assert(p);
|
||||
static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) {
|
||||
assert(d);
|
||||
|
||||
siphash24_compress(&d->dev, sizeof(d->dev), state);
|
||||
siphash24_compress(&d->ino, sizeof(d->ino), state);
|
||||
}
|
||||
|
||||
const struct hash_ops inode_data_hash_ops = {
|
||||
.hash = inode_data_hash_func,
|
||||
.compare = inode_data_compare
|
||||
};
|
||||
DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
|
||||
|
||||
static void event_free_inode_data(
|
||||
sd_event *e,
|
||||
|
|
|
@ -187,16 +187,13 @@ int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
|
|||
return id128_write_fd(fd, f, id, do_sync);
|
||||
}
|
||||
|
||||
void id128_hash_func(const void *p, struct siphash *state) {
|
||||
siphash24_compress(p, 16, state);
|
||||
void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
|
||||
siphash24_compress(p, sizeof(sd_id128_t), state);
|
||||
}
|
||||
|
||||
int id128_compare_func(const void *a, const void *b) {
|
||||
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
|
||||
return memcmp(a, b, 16);
|
||||
}
|
||||
|
||||
const struct hash_ops id128_hash_ops = {
|
||||
.hash = id128_hash_func,
|
||||
.compare = id128_compare_func,
|
||||
};
|
||||
DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
@ -28,6 +28,6 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret);
|
|||
int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync);
|
||||
int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
|
||||
|
||||
void id128_hash_func(const void *p, struct siphash *state);
|
||||
int id128_compare_func(const void *a, const void *b) _pure_;
|
||||
void id128_hash_func(const sd_id128_t *p, struct siphash *state);
|
||||
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
|
||||
extern const struct hash_ops id128_hash_ops;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "dns-domain.h"
|
||||
#include "hashmap.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
|
@ -28,9 +29,9 @@
|
|||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
|
||||
int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
||||
int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) {
|
||||
const char *n;
|
||||
char *d;
|
||||
char *d, last_char = 0;
|
||||
int r = 0;
|
||||
|
||||
assert(name);
|
||||
|
@ -40,14 +41,16 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
d = dest;
|
||||
|
||||
for (;;) {
|
||||
if (*n == '.') {
|
||||
n++;
|
||||
if (*n == 0 || *n == '.') {
|
||||
if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-')
|
||||
/* Trailing dash */
|
||||
return -EINVAL;
|
||||
|
||||
if (*n == '.')
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*n == 0)
|
||||
break;
|
||||
|
||||
if (r >= DNS_LABEL_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -56,6 +59,8 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
|
||||
if (*n == '\\') {
|
||||
/* Escaped character */
|
||||
if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES))
|
||||
return -EINVAL;
|
||||
|
||||
n++;
|
||||
|
||||
|
@ -66,6 +71,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
else if (IN_SET(*n, '\\', '.')) {
|
||||
/* Escaped backslash or dot */
|
||||
|
||||
if (FLAGS_SET(flags, DNS_LABEL_LDH))
|
||||
return -EINVAL;
|
||||
|
||||
last_char = *n;
|
||||
if (d)
|
||||
*(d++) = *n;
|
||||
sz--;
|
||||
|
@ -94,6 +103,11 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
if (k > 255)
|
||||
return -EINVAL;
|
||||
|
||||
if (FLAGS_SET(flags, DNS_LABEL_LDH) &&
|
||||
!valid_ldh_char((char) k))
|
||||
return -EINVAL;
|
||||
|
||||
last_char = (char) k;
|
||||
if (d)
|
||||
*(d++) = (char) k;
|
||||
sz--;
|
||||
|
@ -107,6 +121,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
|||
|
||||
/* Normal character */
|
||||
|
||||
if (FLAGS_SET(flags, DNS_LABEL_LDH)) {
|
||||
if (!valid_ldh_char(*n))
|
||||
return -EINVAL;
|
||||
if (r == 0 && *n == '-')
|
||||
/* Leading dash */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
last_char = *n;
|
||||
if (d)
|
||||
*(d++) = *n;
|
||||
sz--;
|
||||
|
@ -189,7 +212,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
|
|||
terminal--;
|
||||
}
|
||||
|
||||
r = dns_label_unescape(&name, dest, sz);
|
||||
r = dns_label_unescape(&name, dest, sz, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -386,7 +409,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
|
|||
#endif
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int dns_name_concat(const char *a, const char *b, char **_ret) {
|
||||
int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
|
||||
_cleanup_free_ char *ret = NULL;
|
||||
size_t n = 0, allocated = 0;
|
||||
const char *p;
|
||||
|
@ -403,7 +426,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
|
|||
for (;;) {
|
||||
char label[DNS_LABEL_MAX];
|
||||
|
||||
r = dns_label_unescape(&p, label, sizeof(label));
|
||||
r = dns_label_unescape(&p, label, sizeof label, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
|
@ -469,8 +492,7 @@ finish:
|
|||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
void dns_name_hash_func(const void *s, struct siphash *state) {
|
||||
const char *p = s;
|
||||
void dns_name_hash_func(const char *p, struct siphash *state) {
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
@ -478,7 +500,7 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
|
|||
for (;;) {
|
||||
char label[DNS_LABEL_MAX+1];
|
||||
|
||||
r = dns_label_unescape(&p, label, sizeof(label));
|
||||
r = dns_label_unescape(&p, label, sizeof label, 0);
|
||||
if (r < 0)
|
||||
break;
|
||||
if (r == 0)
|
||||
|
@ -493,15 +515,15 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
|
|||
string_hash_func("", state);
|
||||
}
|
||||
|
||||
int dns_name_compare_func(const void *a, const void *b) {
|
||||
int dns_name_compare_func(const char *a, const char *b) {
|
||||
const char *x, *y;
|
||||
int r, q;
|
||||
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
x = (const char *) a + strlen(a);
|
||||
y = (const char *) b + strlen(b);
|
||||
x = a + strlen(a);
|
||||
y = b + strlen(b);
|
||||
|
||||
for (;;) {
|
||||
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
|
||||
|
@ -520,10 +542,7 @@ int dns_name_compare_func(const void *a, const void *b) {
|
|||
}
|
||||
}
|
||||
|
||||
const struct hash_ops dns_name_hash_ops = {
|
||||
.hash = dns_name_hash_func,
|
||||
.compare = dns_name_compare_func
|
||||
};
|
||||
DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
|
||||
|
||||
int dns_name_equal(const char *x, const char *y) {
|
||||
int r, q;
|
||||
|
@ -534,11 +553,11 @@ int dns_name_equal(const char *x, const char *y) {
|
|||
for (;;) {
|
||||
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
|
||||
|
||||
r = dns_label_unescape(&x, la, sizeof(la));
|
||||
r = dns_label_unescape(&x, la, sizeof la, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
q = dns_label_unescape(&y, lb, sizeof(lb));
|
||||
q = dns_label_unescape(&y, lb, sizeof lb, 0);
|
||||
if (q < 0)
|
||||
return q;
|
||||
|
||||
|
@ -565,14 +584,14 @@ int dns_name_endswith(const char *name, const char *suffix) {
|
|||
for (;;) {
|
||||
char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
|
||||
|
||||
r = dns_label_unescape(&n, ln, sizeof(ln));
|
||||
r = dns_label_unescape(&n, ln, sizeof ln, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!saved_n)
|
||||
saved_n = n;
|
||||
|
||||
q = dns_label_unescape(&s, ls, sizeof(ls));
|
||||
q = dns_label_unescape(&s, ls, sizeof ls, 0);
|
||||
if (q < 0)
|
||||
return q;
|
||||
|
||||
|
@ -603,13 +622,13 @@ int dns_name_startswith(const char *name, const char *prefix) {
|
|||
for (;;) {
|
||||
char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
|
||||
|
||||
r = dns_label_unescape(&p, lp, sizeof(lp));
|
||||
r = dns_label_unescape(&p, lp, sizeof lp, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return true;
|
||||
|
||||
q = dns_label_unescape(&n, ln, sizeof(ln));
|
||||
q = dns_label_unescape(&n, ln, sizeof ln, 0);
|
||||
if (q < 0)
|
||||
return q;
|
||||
|
||||
|
@ -638,14 +657,14 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
|
|||
if (!saved_before)
|
||||
saved_before = n;
|
||||
|
||||
r = dns_label_unescape(&n, ln, sizeof(ln));
|
||||
r = dns_label_unescape(&n, ln, sizeof ln, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!saved_after)
|
||||
saved_after = n;
|
||||
|
||||
q = dns_label_unescape(&s, ls, sizeof(ls));
|
||||
q = dns_label_unescape(&s, ls, sizeof ls, 0);
|
||||
if (q < 0)
|
||||
return q;
|
||||
|
||||
|
@ -668,7 +687,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
|
|||
/* Found it! Now generate the new name */
|
||||
prefix = strndupa(name, saved_before - name);
|
||||
|
||||
r = dns_name_concat(prefix, new_suffix, ret);
|
||||
r = dns_name_concat(prefix, new_suffix, 0, ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -746,7 +765,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
|
|||
for (i = 0; i < ELEMENTSOF(a); i++) {
|
||||
char label[DNS_LABEL_MAX+1];
|
||||
|
||||
r = dns_label_unescape(&p, label, sizeof(label));
|
||||
r = dns_label_unescape(&p, label, sizeof label, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
|
@ -783,7 +802,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
|
|||
char label[DNS_LABEL_MAX+1];
|
||||
int x, y;
|
||||
|
||||
r = dns_label_unescape(&p, label, sizeof(label));
|
||||
r = dns_label_unescape(&p, label, sizeof label, 0);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
if (r != 1)
|
||||
|
@ -792,7 +811,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
|
|||
if (x < 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = dns_label_unescape(&p, label, sizeof(label));
|
||||
r = dns_label_unescape(&p, label, sizeof label, 0);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
if (r != 1)
|
||||
|
@ -861,7 +880,7 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo
|
|||
* dns_label_unescape() returns 0 when it hits the end
|
||||
* of the domain name, which we rely on here to encode
|
||||
* the trailing NUL byte. */
|
||||
r = dns_label_unescape(&domain, (char *) out, len);
|
||||
r = dns_label_unescape(&domain, (char *) out, len, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -929,7 +948,7 @@ bool dns_srv_type_is_valid(const char *name) {
|
|||
|
||||
/* This more or less implements RFC 6335, Section 5.1 */
|
||||
|
||||
r = dns_label_unescape(&name, label, sizeof(label));
|
||||
r = dns_label_unescape(&name, label, sizeof label, 0);
|
||||
if (r < 0)
|
||||
return false;
|
||||
if (r == 0)
|
||||
|
@ -989,7 +1008,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha
|
|||
return -EINVAL;
|
||||
|
||||
if (!name)
|
||||
return dns_name_concat(type, domain, ret);
|
||||
return dns_name_concat(type, domain, 0, ret);
|
||||
|
||||
if (!dns_service_name_is_valid(name))
|
||||
return -EINVAL;
|
||||
|
@ -998,11 +1017,11 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_name_concat(type, domain, &n);
|
||||
r = dns_name_concat(type, domain, 0, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return dns_name_concat(escaped, n, ret);
|
||||
return dns_name_concat(escaped, n, 0, ret);
|
||||
}
|
||||
|
||||
static bool dns_service_name_label_is_valid(const char *label, size_t n) {
|
||||
|
@ -1027,7 +1046,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
|
|||
assert(joined);
|
||||
|
||||
/* Get first label from the full name */
|
||||
an = dns_label_unescape(&p, a, sizeof(a));
|
||||
an = dns_label_unescape(&p, a, sizeof(a), 0);
|
||||
if (an < 0)
|
||||
return an;
|
||||
|
||||
|
@ -1035,7 +1054,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
|
|||
x++;
|
||||
|
||||
/* If there was a first label, try to get the second one */
|
||||
bn = dns_label_unescape(&p, b, sizeof(b));
|
||||
bn = dns_label_unescape(&p, b, sizeof(b), 0);
|
||||
if (bn < 0)
|
||||
return bn;
|
||||
|
||||
|
@ -1044,7 +1063,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
|
|||
|
||||
/* If there was a second label, try to get the third one */
|
||||
q = p;
|
||||
cn = dns_label_unescape(&p, c, sizeof(c));
|
||||
cn = dns_label_unescape(&p, c, sizeof(c), 0);
|
||||
if (cn < 0)
|
||||
return cn;
|
||||
|
||||
|
@ -1094,7 +1113,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
|
|||
d = joined;
|
||||
|
||||
finish:
|
||||
r = dns_name_normalize(d, &domain);
|
||||
r = dns_name_normalize(d, 0, &domain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -1110,7 +1129,7 @@ finish:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dns_name_build_suffix_table(const char *name, const char*table[]) {
|
||||
static int dns_name_build_suffix_table(const char *name, const char *table[]) {
|
||||
const char *p;
|
||||
unsigned n = 0;
|
||||
int r;
|
||||
|
@ -1239,12 +1258,12 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
|
|||
}
|
||||
|
||||
x = a_labels[n - 1 - k];
|
||||
r = dns_label_unescape(&x, la, sizeof(la));
|
||||
r = dns_label_unescape(&x, la, sizeof la, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
y = b_labels[m - 1 - k];
|
||||
q = dns_label_unescape(&y, lb, sizeof(lb));
|
||||
q = dns_label_unescape(&y, lb, sizeof lb, 0);
|
||||
if (q < 0)
|
||||
return q;
|
||||
|
||||
|
@ -1312,13 +1331,13 @@ int dns_name_apply_idna(const char *name, char **ret) {
|
|||
for (;;) {
|
||||
char label[DNS_LABEL_MAX];
|
||||
|
||||
r = dns_label_unescape(&name, label, sizeof(label));
|
||||
r = dns_label_unescape(&name, label, sizeof label, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
q = dns_label_apply_idna(label, r, label, sizeof(label));
|
||||
q = dns_label_apply_idna(label, r, label, sizeof label);
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (q > 0)
|
||||
|
|
|
@ -24,13 +24,18 @@
|
|||
/* Maximum number of labels per valid hostname */
|
||||
#define DNS_N_LABELS_MAX 127
|
||||
|
||||
int dns_label_unescape(const char **name, char *dest, size_t sz);
|
||||
typedef enum DNSLabelFlags {
|
||||
DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */
|
||||
DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */
|
||||
} DNSLabelFlags;
|
||||
|
||||
int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags);
|
||||
int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
|
||||
int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
|
||||
int dns_label_escape_new(const char *p, size_t l, char **ret);
|
||||
|
||||
static inline int dns_name_parent(const char **name) {
|
||||
return dns_label_unescape(name, NULL, DNS_LABEL_MAX);
|
||||
return dns_label_unescape(name, NULL, DNS_LABEL_MAX, 0);
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
|
@ -40,18 +45,18 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
|
|||
#endif
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int dns_name_concat(const char *a, const char *b, char **ret);
|
||||
int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret);
|
||||
|
||||
static inline int dns_name_normalize(const char *s, char **ret) {
|
||||
static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **ret) {
|
||||
/* dns_name_concat() normalizes as a side-effect */
|
||||
return dns_name_concat(s, NULL, ret);
|
||||
return dns_name_concat(s, NULL, flags, ret);
|
||||
}
|
||||
|
||||
static inline int dns_name_is_valid(const char *s) {
|
||||
int r;
|
||||
|
||||
/* dns_name_normalize() verifies as a side effect */
|
||||
r = dns_name_normalize(s, NULL);
|
||||
r = dns_name_normalize(s, 0, NULL);
|
||||
if (r == -EINVAL)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
|
@ -59,8 +64,19 @@ static inline int dns_name_is_valid(const char *s) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
void dns_name_hash_func(const void *s, struct siphash *state);
|
||||
int dns_name_compare_func(const void *a, const void *b);
|
||||
static inline int dns_name_is_valid_ldh(const char *s) {
|
||||
int r;
|
||||
|
||||
r = dns_name_concat(s, NULL, DNS_LABEL_LDH|DNS_LABEL_NO_ESCAPES, NULL);
|
||||
if (r == -EINVAL)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dns_name_hash_func(const char *s, struct siphash *state);
|
||||
int dns_name_compare_func(const char *a, const char *b);
|
||||
extern const struct hash_ops dns_name_hash_ops;
|
||||
|
||||
int dns_name_between(const char *a, const char *b, const char *c);
|
||||
|
|
|
@ -23,9 +23,11 @@
|
|||
# error "Do not include _sd-common.h directly; it is a private header."
|
||||
#endif
|
||||
|
||||
typedef void (*_sd_destroy_t)(void *userdata);
|
||||
|
||||
#ifndef _sd_printf_
|
||||
# if __GNUC__ >= 4
|
||||
# define _sd_printf_(a,b) __attribute__ ((__format__(printf, a, b)))
|
||||
# define _sd_printf_(a,b) __attribute__((__format__(printf, a, b)))
|
||||
# else
|
||||
# define _sd_printf_(a,b)
|
||||
# endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <net/ethernet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "sd-dhcp-lease.h"
|
||||
#include "sd-event.h"
|
||||
|
@ -127,12 +128,14 @@ int sd_dhcp_client_set_client_id(
|
|||
size_t data_len);
|
||||
int sd_dhcp_client_set_iaid_duid(
|
||||
sd_dhcp_client *client,
|
||||
bool iaid_set,
|
||||
uint32_t iaid,
|
||||
uint16_t duid_type,
|
||||
const void *duid,
|
||||
size_t duid_len);
|
||||
int sd_dhcp_client_set_iaid_duid_llt(
|
||||
sd_dhcp_client *client,
|
||||
bool iaid_set,
|
||||
uint32_t iaid,
|
||||
uint64_t llt_time);
|
||||
int sd_dhcp_client_set_duid(
|
||||
|
|
|
@ -77,7 +77,7 @@ typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si,
|
|||
typedef void* sd_event_child_handler_t;
|
||||
#endif
|
||||
typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata);
|
||||
typedef void (*sd_event_destroy_t)(void *userdata);
|
||||
typedef _sd_destroy_t sd_event_destroy_t;
|
||||
|
||||
int sd_event_default(sd_event **e);
|
||||
|
||||
|
|
|
@ -41,19 +41,20 @@ int sd_id128_from_string(const char *s, sd_id128_t *ret);
|
|||
int sd_id128_randomize(sd_id128_t *ret);
|
||||
|
||||
int sd_id128_get_machine(sd_id128_t *ret);
|
||||
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
|
||||
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
|
||||
int sd_id128_get_boot(sd_id128_t *ret);
|
||||
int sd_id128_get_invocation(sd_id128_t *ret);
|
||||
|
||||
#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
|
||||
((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
|
||||
0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }})
|
||||
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
|
||||
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
|
||||
|
||||
#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
|
||||
{ .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
|
||||
0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}
|
||||
|
||||
#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
|
||||
((const sd_id128_t) SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15))
|
||||
|
||||
|
||||
/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16
|
||||
* times. It is hence not a good idea to call this macro with an
|
||||
* expensive function as parameter or an expression with side
|
||||
|
@ -109,7 +110,12 @@ _sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
|
|||
return a.qwords[0] == 0 && a.qwords[1] == 0;
|
||||
}
|
||||
|
||||
_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
|
||||
return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF);
|
||||
}
|
||||
|
||||
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
|
||||
#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
|
|
Loading…
Reference in a new issue