systemd: move basic systemd library to shared/nm-utils

For better or worse, we already pull in large parts of systemd sources.

I need a base64 decode implementation (because glib's g_base64_decode()
cannot reject invalid encodings). Instead of coming up with my own or
copy-paste if from somewhere, reuse systemd's unbase64mem().

But for that, make systemd's basic bits an independent static library
first because I will need it in libnm-core.

This doesn't really change anything except making "libnm-systemd-core.la"
an indpendent static library that could be used from "libnm-core". We
shall still be mindful about which internal code of systemd we use, and only
access functionality that is exposed via "systemd/nm-sd-utils-shared.h".
This commit is contained in:
Thomas Haller
2018-12-28 18:11:16 +01:00
parent 616abe865d
commit 2c537b9d21
145 changed files with 564 additions and 383 deletions

View File

@@ -0,0 +1,83 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <stdint.h>
#include <string.h>
#include "alloc-util.h"
#include "macro.h"
#include "util.h"
void* memdup(const void *p, size_t l) {
void *ret;
assert(l == 0 || p);
ret = malloc(l ?: 1);
if (!ret)
return NULL;
memcpy(ret, p, l);
return ret;
}
void* memdup_suffix0(const void *p, size_t l) {
void *ret;
assert(l == 0 || p);
/* The same as memdup() but place a safety NUL byte after the allocated memory */
ret = malloc(l + 1);
if (!ret)
return NULL;
*((uint8_t*) mempcpy(ret, p, l)) = 0;
return ret;
}
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
size_t a, newalloc;
void *q;
assert(p);
assert(allocated);
if (*allocated >= need)
return *p;
newalloc = MAX(need * 2, 64u / size);
a = newalloc * size;
/* check for overflows */
if (a < size * need)
return NULL;
q = realloc(*p, a);
if (!q)
return NULL;
*p = q;
*allocated = newalloc;
return q;
}
void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
size_t prev;
uint8_t *q;
assert(p);
assert(allocated);
prev = *allocated;
q = greedy_realloc(p, allocated, need, size);
if (!q)
return NULL;
if (*allocated > prev)
memzero(q + prev * size, (*allocated - prev) * size);
return q;
}

View File

@@ -0,0 +1,147 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <alloca.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#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) ?: 1, sizeof(t)))
#define newa(t, n) \
({ \
assert(!size_multiply_overflow(sizeof(t), n)); \
(t*) alloca(sizeof(t)*(n)); \
})
#define newa0(t, n) \
({ \
assert(!size_multiply_overflow(sizeof(t), n)); \
(t*) alloca0(sizeof(t)*(n)); \
})
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
#define malloc0(n) (calloc(1, (n)))
static inline void *mfree(void *memory) {
free(memory);
return NULL;
}
#define free_and_replace(a, b) \
({ \
free(a); \
(a) = (b); \
(b) = NULL; \
0; \
})
void* memdup(const void *p, size_t l) _alloc_(2);
void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
#define memdupa(p, l) \
({ \
void *_q_; \
_q_ = alloca(l); \
memcpy(_q_, p, l); \
})
#define memdupa_suffix0(p, l) \
({ \
void *_q_; \
_q_ = alloca(l + 1); \
((uint8_t*) _q_)[l] = 0; \
memcpy(_q_, p, l); \
})
static inline void freep(void *p) {
free(*(void**) p);
}
#define _cleanup_free_ _cleanup_(freep)
static inline bool size_multiply_overflow(size_t size, size_t need) {
return _unlikely_(need != 0 && size > (SIZE_MAX / need));
}
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) {
if (size_multiply_overflow(size, need))
return NULL;
return malloc(size * need ?: 1);
}
#if !HAVE_REALLOCARRAY
_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 ?: 1);
}
#endif
_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
if (size_multiply_overflow(size, need))
return NULL;
return memdup(p, size * need);
}
_alloc_(2, 3) static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) {
if (size_multiply_overflow(size, need))
return NULL;
return memdup_suffix0(p, size * need);
}
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
#define GREEDY_REALLOC(array, allocated, need) \
greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
#define GREEDY_REALLOC0(array, allocated, need) \
greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
#define alloca0(n) \
({ \
char *_new_; \
size_t _len_ = n; \
_new_ = alloca(_len_); \
(void *) memset(_new_, 0, _len_); \
})
/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
#define alloca_align(size, align) \
({ \
void *_ptr_; \
size_t _mask_ = (align) - 1; \
_ptr_ = alloca((size) + _mask_); \
(void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
})
#define alloca0_align(size, align) \
({ \
void *_new_; \
size_t _size_ = (size); \
_new_ = alloca_align(_size_, (align)); \
(void*)memset(_new_, 0, _size_); \
})
/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to
* NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
#define TAKE_PTR(ptr) \
({ \
typeof(ptr) _ptr_ = (ptr); \
(ptr) = NULL; \
_ptr_; \
})

View File

@@ -0,0 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
int asynchronous_job(void* (*func)(void *p), void *arg);
int asynchronous_sync(pid_t *ret_pid);
int asynchronous_close(int fd);

View File

@@ -0,0 +1,578 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.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 */

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

View File

@@ -0,0 +1,760 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "alloc-util.h"
#include "env-util.h"
#include "escape.h"
#include "extract-word.h"
#include "macro.h"
#include "parse-util.h"
#include "string-util.h"
#include "strv.h"
#include "utf8.h"
#if 0 /* NM_IGNORED */
#define VALID_CHARS_ENV_NAME \
DIGITS LETTERS \
"_"
static bool env_name_is_valid_n(const char *e, size_t n) {
const char *p;
if (!e)
return false;
if (n <= 0)
return false;
if (e[0] >= '0' && e[0] <= '9')
return false;
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, an individual assignment hence cannot be
* either. Discounting the equal sign and trailing NUL this
* hence leaves ARG_MAX-2 as longest possible variable
* name. */
if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
return false;
for (p = e; p < e + n; p++)
if (!strchr(VALID_CHARS_ENV_NAME, *p))
return false;
return true;
}
bool env_name_is_valid(const char *e) {
if (!e)
return false;
return env_name_is_valid_n(e, strlen(e));
}
bool env_value_is_valid(const char *e) {
if (!e)
return false;
if (!utf8_is_valid(e))
return false;
/* bash allows tabs and newlines in environment variables, and so
* should we */
if (string_has_cc(e, "\t\n"))
return false;
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, an individual assignment hence cannot be
* either. Discounting the shortest possible variable name of
* length 1, the equal sign and trailing NUL this hence leaves
* ARG_MAX-3 as longest possible variable value. */
if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3)
return false;
return true;
}
bool env_assignment_is_valid(const char *e) {
const char *eq;
eq = strchr(e, '=');
if (!eq)
return false;
if (!env_name_is_valid_n(e, eq - e))
return false;
if (!env_value_is_valid(eq + 1))
return false;
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, hence the individual variable assignments
* cannot be either, but let's leave room for one trailing NUL
* byte. */
if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1)
return false;
return true;
}
bool strv_env_is_valid(char **e) {
char **p, **q;
STRV_FOREACH(p, e) {
size_t k;
if (!env_assignment_is_valid(*p))
return false;
/* Check if there are duplicate assignments */
k = strcspn(*p, "=");
STRV_FOREACH(q, p + 1)
if (strneq(*p, *q, k) && (*q)[k] == '=')
return false;
}
return true;
}
bool strv_env_name_is_valid(char **l) {
char **p;
STRV_FOREACH(p, l) {
if (!env_name_is_valid(*p))
return false;
if (strv_contains(p + 1, *p))
return false;
}
return true;
}
bool strv_env_name_or_assignment_is_valid(char **l) {
char **p;
STRV_FOREACH(p, l) {
if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
return false;
if (strv_contains(p + 1, *p))
return false;
}
return true;
}
static int env_append(char **r, char ***k, char **a) {
assert(r);
assert(k);
assert(*k >= r);
if (!a)
return 0;
/* Expects the following arguments: 'r' shall point to the beginning of an strv we are going to append to, 'k'
* to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv.
*
* This call adds every entry of 'a' to 'r', either overriding an existing matching entry, or appending to it.
*
* This call assumes 'r' has enough pre-allocated space to grow by all of 'a''s items. */
for (; *a; a++) {
char **j, *c;
size_t n;
n = strcspn(*a, "=");
if ((*a)[n] == '=')
n++;
for (j = r; j < *k; j++)
if (strneq(*j, *a, n))
break;
c = strdup(*a);
if (!c)
return -ENOMEM;
if (j >= *k) { /* Append to the end? */
(*k)[0] = c;
(*k)[1] = NULL;
(*k)++;
} else
free_and_replace(*j, c); /* Override existing item */
}
return 0;
}
char **strv_env_merge(size_t n_lists, ...) {
_cleanup_strv_free_ char **ret = NULL;
size_t n = 0, i;
char **l, **k;
va_list ap;
/* Merges an arbitrary number of environment sets */
va_start(ap, n_lists);
for (i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
n += strv_length(l);
}
va_end(ap);
ret = new(char*, n+1);
if (!ret)
return NULL;
*ret = NULL;
k = ret;
va_start(ap, n_lists);
for (i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
if (env_append(ret, &k, l) < 0) {
va_end(ap);
return NULL;
}
}
va_end(ap);
return TAKE_PTR(ret);
}
static bool env_match(const char *t, const char *pattern) {
assert(t);
assert(pattern);
/* pattern a matches string a
* a matches a=
* a matches a=b
* a= matches a=
* a=b matches a=b
* a= does not match a
* a=b does not match a=
* a=b does not match a
* a=b does not match a=c */
if (streq(t, pattern))
return true;
if (!strchr(pattern, '=')) {
size_t l = strlen(pattern);
return strneq(t, pattern, l) && t[l] == '=';
}
return false;
}
static bool env_entry_has_name(const char *entry, const char *name) {
const char *t;
assert(entry);
assert(name);
t = startswith(entry, name);
if (!t)
return false;
return *t == '=';
}
char **strv_env_delete(char **x, size_t n_lists, ...) {
size_t n, i = 0;
char **k, **r;
va_list ap;
/* Deletes every entry from x that is mentioned in the other
* string lists */
n = strv_length(x);
r = new(char*, n+1);
if (!r)
return NULL;
STRV_FOREACH(k, x) {
size_t v;
va_start(ap, n_lists);
for (v = 0; v < n_lists; v++) {
char **l, **j;
l = va_arg(ap, char**);
STRV_FOREACH(j, l)
if (env_match(*k, *j))
goto skip;
}
va_end(ap);
r[i] = strdup(*k);
if (!r[i]) {
strv_free(r);
return NULL;
}
i++;
continue;
skip:
va_end(ap);
}
r[i] = NULL;
assert(i <= n);
return r;
}
char **strv_env_unset(char **l, const char *p) {
char **f, **t;
if (!l)
return NULL;
assert(p);
/* Drops every occurrence of the env var setting p in the
* string list. Edits in-place. */
for (f = t = l; *f; f++) {
if (env_match(*f, p)) {
free(*f);
continue;
}
*(t++) = *f;
}
*t = NULL;
return l;
}
char **strv_env_unset_many(char **l, ...) {
char **f, **t;
if (!l)
return NULL;
/* Like strv_env_unset() but applies many at once. Edits in-place. */
for (f = t = l; *f; f++) {
bool found = false;
const char *p;
va_list ap;
va_start(ap, l);
while ((p = va_arg(ap, const char*))) {
if (env_match(*f, p)) {
found = true;
break;
}
}
va_end(ap);
if (found) {
free(*f);
continue;
}
*(t++) = *f;
}
*t = NULL;
return l;
}
int strv_env_replace(char ***l, char *p) {
const char *t, *name;
char **f;
int r;
assert(p);
/* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits
* in-place. Does not copy p. p must be a valid key=value assignment.
*/
t = strchr(p, '=');
if (!t)
return -EINVAL;
name = strndupa(p, t - p);
STRV_FOREACH(f, *l)
if (env_entry_has_name(*f, name)) {
free_and_replace(*f, p);
strv_env_unset(f + 1, *f);
return 0;
}
/* We didn't find a match, we need to append p or create a new strv */
r = strv_push(l, p);
if (r < 0)
return r;
return 1;
}
char **strv_env_set(char **x, const char *p) {
_cleanup_strv_free_ char **ret = NULL;
size_t n, m;
char **k;
/* Overrides the env var setting of p, returns a new copy */
n = strv_length(x);
m = n + 2;
if (m < n) /* overflow? */
return NULL;
ret = new(char*, m);
if (!ret)
return NULL;
*ret = NULL;
k = ret;
if (env_append(ret, &k, x) < 0)
return NULL;
if (env_append(ret, &k, STRV_MAKE(p)) < 0)
return NULL;
return TAKE_PTR(ret);
}
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
char **i;
assert(name);
if (k <= 0)
return NULL;
STRV_FOREACH_BACKWARDS(i, l)
if (strneq(*i, name, k) &&
(*i)[k] == '=')
return *i + k + 1;
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t;
t = strndupa(name, k);
return getenv(t);
};
return NULL;
}
char *strv_env_get(char **l, const char *name) {
assert(name);
return strv_env_get_n(l, name, strlen(name), 0);
}
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
char **p, **q;
int k = 0;
STRV_FOREACH(p, e) {
size_t n;
bool duplicate = false;
if (!env_assignment_is_valid(*p)) {
if (invalid_callback)
invalid_callback(*p, userdata);
free(*p);
continue;
}
n = strcspn(*p, "=");
STRV_FOREACH(q, p + 1)
if (strneq(*p, *q, n) && (*q)[n] == '=') {
duplicate = true;
break;
}
if (duplicate) {
free(*p);
continue;
}
e[k++] = *p;
}
if (e)
e[k] = NULL;
return e;
}
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
enum {
WORD,
CURLY,
VARIABLE,
VARIABLE_RAW,
TEST,
DEFAULT_VALUE,
ALTERNATE_VALUE,
} state = WORD;
const char *e, *word = format, *test_value;
char *k;
_cleanup_free_ char *r = NULL;
size_t i, len;
int nest = 0;
assert(format);
for (e = format, i = 0; *e && i < n; e ++, i ++)
switch (state) {
case WORD:
if (*e == '$')
state = CURLY;
break;
case CURLY:
if (*e == '{') {
k = strnappend(r, word, e-word-1);
if (!k)
return NULL;
free_and_replace(r, k);
word = e-1;
state = VARIABLE;
nest++;
} else if (*e == '$') {
k = strnappend(r, word, e-word);
if (!k)
return NULL;
free_and_replace(r, k);
word = e+1;
state = WORD;
} else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
k = strnappend(r, word, e-word-1);
if (!k)
return NULL;
free_and_replace(r, k);
word = e-1;
state = VARIABLE_RAW;
} else
state = WORD;
break;
case VARIABLE:
if (*e == '}') {
const char *t;
t = strv_env_get_n(env, word+2, e-word-2, flags);
k = strappend(r, t);
if (!k)
return NULL;
free_and_replace(r, k);
word = e+1;
state = WORD;
} else if (*e == ':') {
if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
/* Treat this as unsupported syntax, i.e. do no replacement */
state = WORD;
else {
len = e-word-2;
state = TEST;
}
}
break;
case TEST:
if (*e == '-')
state = DEFAULT_VALUE;
else if (*e == '+')
state = ALTERNATE_VALUE;
else {
state = WORD;
break;
}
test_value = e+1;
break;
case DEFAULT_VALUE: /* fall through */
case ALTERNATE_VALUE:
assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
if (*e == '{') {
nest++;
break;
}
if (*e != '}')
break;
nest--;
if (nest == 0) {
const char *t;
_cleanup_free_ char *v = NULL;
t = strv_env_get_n(env, word+2, len, flags);
if (t && state == ALTERNATE_VALUE)
t = v = replace_env_n(test_value, e-test_value, env, flags);
else if (!t && state == DEFAULT_VALUE)
t = v = replace_env_n(test_value, e-test_value, env, flags);
k = strappend(r, t);
if (!k)
return NULL;
free_and_replace(r, k);
word = e+1;
state = WORD;
}
break;
case VARIABLE_RAW:
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
const char *t;
t = strv_env_get_n(env, word+1, e-word-1, flags);
k = strappend(r, t);
if (!k)
return NULL;
free_and_replace(r, k);
word = e--;
i--;
state = WORD;
}
break;
}
if (state == VARIABLE_RAW) {
const char *t;
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
t = strv_env_get_n(env, word+1, e-word-1, flags);
return strappend(r, t);
} else
return strnappend(r, word, e-word);
}
char **replace_env_argv(char **argv, char **env) {
char **ret, **i;
size_t k = 0, l = 0;
l = strv_length(argv);
ret = new(char*, l+1);
if (!ret)
return NULL;
STRV_FOREACH(i, argv) {
/* If $FOO appears as single word, replace it by the split up variable */
if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
char *e;
char **w, **m = NULL;
size_t q;
e = strv_env_get(env, *i+1);
if (e) {
int r;
r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);
return NULL;
}
} else
m = NULL;
q = strv_length(m);
l = l + q - 1;
w = reallocarray(ret, l + 1, sizeof(char *));
if (!w) {
ret[k] = NULL;
strv_free(ret);
strv_free(m);
return NULL;
}
ret = w;
if (m) {
memcpy(ret + k, m, q * sizeof(char*));
free(m);
}
k += q;
continue;
}
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
ret[k] = replace_env(*i, env, 0);
if (!ret[k]) {
strv_free(ret);
return NULL;
}
k++;
}
ret[k] = NULL;
return ret;
}
#endif /* NM_IGNORED */
int getenv_bool(const char *p) {
const char *e;
e = getenv(p);
if (!e)
return -ENXIO;
return parse_boolean(e);
}
#if 0 /* NM_IGNORED */
int getenv_bool_secure(const char *p) {
const char *e;
e = secure_getenv(p);
if (!e)
return -ENXIO;
return parse_boolean(e);
}
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include "macro.h"
#include "string.h"
bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
enum {
REPLACE_ENV_USE_ENVIRONMENT = 1u,
REPLACE_ENV_ALLOW_BRACELESS = 2u,
REPLACE_ENV_ALLOW_EXTENDED = 4u,
};
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
char **replace_env_argv(char **argv, char **env);
static inline char *replace_env(const char *format, char **env, unsigned flags) {
return replace_env_n(format, strlen(format), env, flags);
}
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
bool strv_env_name_is_valid(char **l);
bool strv_env_name_or_assignment_is_valid(char **l);
char **strv_env_merge(size_t n_lists, ...);
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char **strv_env_set(char **x, const char *p); /* New copy ... */
char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
int strv_env_replace(char ***l, char *p); /* In place ... */
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
int getenv_bool(const char *p);
int getenv_bool_secure(const char *p);

View File

@@ -0,0 +1,508 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "alloc-util.h"
#include "escape.h"
#include "hexdecoct.h"
#include "macro.h"
#include "utf8.h"
int cescape_char(char c, char *buf) {
char *buf_old = buf;
/* Needs space for 4 characters in the buffer */
switch (c) {
case '\a':
*(buf++) = '\\';
*(buf++) = 'a';
break;
case '\b':
*(buf++) = '\\';
*(buf++) = 'b';
break;
case '\f':
*(buf++) = '\\';
*(buf++) = 'f';
break;
case '\n':
*(buf++) = '\\';
*(buf++) = 'n';
break;
case '\r':
*(buf++) = '\\';
*(buf++) = 'r';
break;
case '\t':
*(buf++) = '\\';
*(buf++) = 't';
break;
case '\v':
*(buf++) = '\\';
*(buf++) = 'v';
break;
case '\\':
*(buf++) = '\\';
*(buf++) = '\\';
break;
case '"':
*(buf++) = '\\';
*(buf++) = '"';
break;
case '\'':
*(buf++) = '\\';
*(buf++) = '\'';
break;
default:
/* For special chars we prefer octal over
* hexadecimal encoding, simply because glib's
* g_strescape() does the same */
if ((c < ' ') || (c >= 127)) {
*(buf++) = '\\';
*(buf++) = octchar((unsigned char) c >> 6);
*(buf++) = octchar((unsigned char) c >> 3);
*(buf++) = octchar((unsigned char) c);
} else
*(buf++) = c;
break;
}
return buf - buf_old;
}
char *cescape_length(const char *s, size_t n) {
const char *f;
char *r, *t;
assert(s || n == 0);
/* Does C style string escaping. May be reversed with
* cunescape(). */
r = new(char, n*4 + 1);
if (!r)
return NULL;
for (f = s, t = r; f < s + n; f++)
t += cescape_char(*f, t);
*t = 0;
return r;
}
char *cescape(const char *s) {
assert(s);
return cescape_length(s, strlen(s));
}
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) {
int r = 1;
assert(p);
assert(ret);
/* Unescapes C style. Returns the unescaped character in ret.
* Sets *eight_bit to true if the escaped sequence either fits in
* one byte in UTF-8 or is a non-unicode literal byte and should
* instead be copied directly.
*/
if (length != (size_t) -1 && length < 1)
return -EINVAL;
switch (p[0]) {
case 'a':
*ret = '\a';
break;
case 'b':
*ret = '\b';
break;
case 'f':
*ret = '\f';
break;
case 'n':
*ret = '\n';
break;
case 'r':
*ret = '\r';
break;
case 't':
*ret = '\t';
break;
case 'v':
*ret = '\v';
break;
case '\\':
*ret = '\\';
break;
case '"':
*ret = '"';
break;
case '\'':
*ret = '\'';
break;
case 's':
/* This is an extension of the XDG syntax files */
*ret = ' ';
break;
case 'x': {
/* hexadecimal encoding */
int a, b;
if (length != (size_t) -1 && length < 3)
return -EINVAL;
a = unhexchar(p[1]);
if (a < 0)
return -EINVAL;
b = unhexchar(p[2]);
if (b < 0)
return -EINVAL;
/* Don't allow NUL bytes */
if (a == 0 && b == 0)
return -EINVAL;
*ret = (a << 4U) | b;
*eight_bit = true;
r = 3;
break;
}
case 'u': {
/* C++11 style 16bit unicode */
int a[4];
size_t i;
uint32_t c;
if (length != (size_t) -1 && length < 5)
return -EINVAL;
for (i = 0; i < 4; i++) {
a[i] = unhexchar(p[1 + i]);
if (a[i] < 0)
return a[i];
}
c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
/* Don't allow 0 chars */
if (c == 0)
return -EINVAL;
*ret = c;
r = 5;
break;
}
case 'U': {
/* C++11 style 32bit unicode */
int a[8];
size_t i;
char32_t c;
if (length != (size_t) -1 && length < 9)
return -EINVAL;
for (i = 0; i < 8; i++) {
a[i] = unhexchar(p[1 + i]);
if (a[i] < 0)
return a[i];
}
c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
/* Don't allow 0 chars */
if (c == 0)
return -EINVAL;
/* Don't allow invalid code points */
if (!unichar_is_valid(c))
return -EINVAL;
*ret = c;
r = 9;
break;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': {
/* octal encoding */
int a, b, c;
char32_t m;
if (length != (size_t) -1 && length < 3)
return -EINVAL;
a = unoctchar(p[0]);
if (a < 0)
return -EINVAL;
b = unoctchar(p[1]);
if (b < 0)
return -EINVAL;
c = unoctchar(p[2]);
if (c < 0)
return -EINVAL;
/* don't allow NUL bytes */
if (a == 0 && b == 0 && c == 0)
return -EINVAL;
/* Don't allow bytes above 255 */
m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
if (m > 255)
return -EINVAL;
*ret = m;
*eight_bit = true;
r = 3;
break;
}
default:
return -EINVAL;
}
return r;
}
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
char *r, *t;
const char *f;
size_t pl;
assert(s);
assert(ret);
/* Undoes C style string escaping, and optionally prefixes it. */
pl = strlen_ptr(prefix);
r = new(char, pl+length+1);
if (!r)
return -ENOMEM;
if (prefix)
memcpy(r, prefix, pl);
for (f = s, t = r + pl; f < s + length; f++) {
size_t remaining;
bool eight_bit = false;
char32_t u;
int k;
remaining = s + length - f;
assert(remaining > 0);
if (*f != '\\') {
/* A literal, copy verbatim */
*(t++) = *f;
continue;
}
if (remaining == 1) {
if (flags & UNESCAPE_RELAX) {
/* A trailing backslash, copy verbatim */
*(t++) = *f;
continue;
}
free(r);
return -EINVAL;
}
k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit);
if (k < 0) {
if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
continue;
}
free(r);
return k;
}
f += k;
if (eight_bit)
/* One byte? Set directly as specified */
*(t++) = u;
else
/* Otherwise encode as multi-byte UTF-8 */
t += utf8_encode_unichar(t, u);
}
*t = 0;
*ret = r;
return t - r;
}
int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
return cunescape_length_with_prefix(s, length, NULL, flags, ret);
}
int cunescape(const char *s, UnescapeFlags flags, char **ret) {
return cunescape_length(s, strlen(s), flags, ret);
}
char *xescape(const char *s, const char *bad) {
char *r, *t;
const char *f;
/* Escapes all chars in bad, in addition to \ and all special
* chars, in \xFF style escaping. May be reversed with
* cunescape(). */
r = new(char, strlen(s) * 4 + 1);
if (!r)
return NULL;
for (f = s, t = r; *f; f++) {
if ((*f < ' ') || (*f >= 127) ||
(*f == '\\') || strchr(bad, *f)) {
*(t++) = '\\';
*(t++) = 'x';
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
} else
*(t++) = *f;
}
*t = 0;
return r;
}
char *octescape(const char *s, size_t len) {
char *r, *t;
const char *f;
/* Escapes all chars in bad, in addition to \ and " chars,
* in \nnn style escaping. */
r = new(char, len * 4 + 1);
if (!r)
return NULL;
for (f = s, t = r; f < s + len; f++) {
if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) {
*(t++) = '\\';
*(t++) = '0' + (*f >> 6);
*(t++) = '0' + ((*f >> 3) & 8);
*(t++) = '0' + (*f & 8);
} else
*(t++) = *f;
}
*t = 0;
return r;
}
static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) {
assert(bad);
for (; *s; s++) {
if (escape_tab_nl && IN_SET(*s, '\n', '\t')) {
*(t++) = '\\';
*(t++) = *s == '\n' ? 'n' : 't';
continue;
}
if (*s == '\\' || strchr(bad, *s))
*(t++) = '\\';
*(t++) = *s;
}
return t;
}
char *shell_escape(const char *s, const char *bad) {
char *r, *t;
r = new(char, strlen(s)*2+1);
if (!r)
return NULL;
t = strcpy_backslash_escaped(r, s, bad, false);
*t = 0;
return r;
}
char* shell_maybe_quote(const char *s, EscapeStyle style) {
const char *p;
char *r, *t;
assert(s);
/* Encloses a string in quotes if necessary to make it OK as a shell
* string. Note that we treat benign UTF-8 characters as needing
* escaping too, but that should be OK. */
for (p = s; *p; p++)
if (*p <= ' ' ||
*p >= 127 ||
strchr(SHELL_NEED_QUOTES, *p))
break;
if (!*p)
return strdup(s);
r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1);
if (!r)
return NULL;
t = r;
if (style == ESCAPE_BACKSLASH)
*(t++) = '"';
else if (style == ESCAPE_POSIX) {
*(t++) = '$';
*(t++) = '\'';
} else
assert_not_reached("Bad EscapeStyle");
t = mempcpy(t, s, p - s);
if (style == ESCAPE_BACKSLASH)
t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false);
else
t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true);
if (style == ESCAPE_BACKSLASH)
*(t++) = '"';
else
*(t++) = '\'';
*t = 0;
return r;
}

View File

@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <uchar.h>
#include "string-util.h"
#include "missing_type.h"
/* What characters are special in the shell? */
/* must be escaped outside and inside double-quotes */
#define SHELL_NEED_ESCAPE "\"\\`$"
/* Those that can be escaped or double-quoted.
*
* Stricly speaking, ! does not need to be escaped, except in interactive
* mode, but let's be extra nice to the user and quote ! in case this
* output is ever used in interactive mode. */
#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;!"
/* Note that we assume control characters would need to be escaped too in
* addition to the "special" characters listed here, if they appear in the
* string. Current users disallow control characters. Also '"' shall not
* be escaped.
*/
#define SHELL_NEED_ESCAPE_POSIX "\\\'"
typedef enum UnescapeFlags {
UNESCAPE_RELAX = 1,
} UnescapeFlags;
typedef enum EscapeStyle {
ESCAPE_BACKSLASH = 1,
ESCAPE_POSIX = 2,
} EscapeStyle;
char *cescape(const char *s);
char *cescape_length(const char *s, size_t n);
int cescape_char(char c, char *buf);
int cunescape(const char *s, UnescapeFlags flags, char **ret);
int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit);
char *xescape(const char *s, const char *bad);
char *octescape(const char *s, size_t len);
char *shell_escape(const char *s, const char *bad);
char* shell_maybe_quote(const char *s, EscapeStyle style);

View File

@@ -0,0 +1,113 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <net/ethernet.h>
#include <stdio.h>
#include <sys/types.h>
#include "ether-addr-util.h"
#include "macro.h"
#include "string-util.h"
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
assert(addr);
assert(buffer);
/* Like ether_ntoa() but uses %02x instead of %x to print
* ethernet addresses, which makes them look less funny. Also,
* doesn't use a static buffer. */
sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
addr->ether_addr_octet[0],
addr->ether_addr_octet[1],
addr->ether_addr_octet[2],
addr->ether_addr_octet[3],
addr->ether_addr_octet[4],
addr->ether_addr_octet[5]);
return buffer;
}
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 struct ether_addr *p, struct siphash *state) {
siphash24_compress(p, sizeof(struct ether_addr), state);
}
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;
char sep = '\0';
const char *hex = HEXDIGITS, *hexoff;
size_t x;
bool touched;
#define parse_fields(v) \
for (field = 0; field < ELEMENTSOF(v); field++) { \
touched = false; \
for (n = 0; n < (2 * sizeof(v[0])); n++) { \
if (s[pos] == '\0') \
break; \
hexoff = strchr(hex, s[pos]); \
if (!hexoff) \
break; \
assert(hexoff >= hex); \
x = hexoff - hex; \
if (x >= 16) \
x -= 6; /* A-F */ \
assert(x < 16); \
touched = true; \
v[field] <<= 4; \
v[field] += x; \
pos++; \
} \
if (!touched) \
return -EINVAL; \
if (field < (ELEMENTSOF(v)-1)) { \
if (s[pos] != sep) \
return -EINVAL; \
else \
pos++; \
} \
}
assert(s);
assert(ret);
s += strspn(s, WHITESPACE);
sep = s[strspn(s, hex)];
if (sep == '.') {
uint16_t shorts[3] = { 0 };
parse_fields(shorts);
if (s[pos] != '\0')
return -EINVAL;
for (n = 0; n < ELEMENTSOF(shorts); n++) {
ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8);
ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff);
}
} else if (IN_SET(sep, ':', '-')) {
struct ether_addr out = ETHER_ADDR_NULL;
parse_fields(out.ether_addr_octet);
if (s[pos] != '\0')
return -EINVAL;
for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++)
ret->ether_addr_octet[n] = out.ether_addr_octet[n];
} else
return -EINVAL;
return 0;
}

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <net/ethernet.h>
#include <stdbool.h>
#include "hash-funcs.h"
#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
#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 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;
}
#define ETHER_ADDR_NULL ((const struct ether_addr){})
static inline bool ether_addr_is_null(const struct ether_addr *addr) {
return ether_addr_equal(addr, &ETHER_ADDR_NULL);
}
int ether_addr_from_string(const char *s, struct ether_addr *ret);
extern const struct hash_ops ether_addr_hash_ops;

View File

@@ -0,0 +1,289 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "alloc-util.h"
#include "escape.h"
#include "extract-word.h"
#include "log.h"
#include "macro.h"
#include "string-util.h"
#include "utf8.h"
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
_cleanup_free_ char *s = NULL;
size_t allocated = 0, sz = 0;
char c;
int r;
char quote = 0; /* 0 or ' or " */
bool backslash = false; /* whether we've just seen a backslash */
assert(p);
assert(ret);
/* Bail early if called after last value or with no input */
if (!*p)
goto finish;
c = **p;
if (!separators)
separators = WHITESPACE;
/* Parses the first word of a string, and returns it in
* *ret. Removes all quotes in the process. When parsing fails
* (because of an uneven number of quotes or similar), leaves
* the pointer *p at the first invalid character. */
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
if (!GREEDY_REALLOC(s, allocated, sz+1))
return -ENOMEM;
for (;; (*p)++, c = **p) {
if (c == 0)
goto finish_force_terminate;
else if (strchr(separators, c)) {
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
(*p)++;
goto finish_force_next;
}
} else {
/* We found a non-blank character, so we will always
* want to return a string (even if it is empty),
* allocate it here. */
if (!GREEDY_REALLOC(s, allocated, sz+1))
return -ENOMEM;
break;
}
}
for (;; (*p)++, c = **p) {
if (backslash) {
if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
if (c == 0) {
if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
(!quote || flags & EXTRACT_RELAX)) {
/* If we find an unquoted trailing backslash and we're in
* EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
* output.
*
* Unbalanced quotes will only be allowed in EXTRACT_RELAX
* mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
*/
s[sz++] = '\\';
goto finish_force_terminate;
}
if (flags & EXTRACT_RELAX)
goto finish_force_terminate;
return -EINVAL;
}
if (flags & EXTRACT_CUNESCAPE) {
bool eight_bit = false;
char32_t u;
r = cunescape_one(*p, (size_t) -1, &u, &eight_bit);
if (r < 0) {
if (flags & EXTRACT_CUNESCAPE_RELAX) {
s[sz++] = '\\';
s[sz++] = c;
} else
return -EINVAL;
} else {
(*p) += r - 1;
if (eight_bit)
s[sz++] = u;
else
sz += utf8_encode_unichar(s + sz, u);
}
} else
s[sz++] = c;
backslash = false;
} else if (quote) { /* inside either single or double quotes */
for (;; (*p)++, c = **p) {
if (c == 0) {
if (flags & EXTRACT_RELAX)
goto finish_force_terminate;
return -EINVAL;
} else if (c == quote) { /* found the end quote */
quote = 0;
break;
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
backslash = true;
break;
} else {
if (!GREEDY_REALLOC(s, allocated, sz+2))
return -ENOMEM;
s[sz++] = c;
}
}
} else {
for (;; (*p)++, c = **p) {
if (c == 0)
goto finish_force_terminate;
else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_QUOTES)) {
quote = c;
break;
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
backslash = true;
break;
} else if (strchr(separators, c)) {
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
(*p)++;
goto finish_force_next;
}
/* Skip additional coalesced separators. */
for (;; (*p)++, c = **p) {
if (c == 0)
goto finish_force_terminate;
if (!strchr(separators, c))
break;
}
goto finish;
} else {
if (!GREEDY_REALLOC(s, allocated, sz+2))
return -ENOMEM;
s[sz++] = c;
}
}
}
}
finish_force_terminate:
*p = NULL;
finish:
if (!s) {
*p = NULL;
*ret = NULL;
return 0;
}
finish_force_next:
s[sz] = 0;
*ret = TAKE_PTR(s);
return 1;
}
#if 0 /* NM_IGNORED */
int extract_first_word_and_warn(
const char **p,
char **ret,
const char *separators,
ExtractFlags flags,
const char *unit,
const char *filename,
unsigned line,
const char *rvalue) {
/* Try to unquote it, if it fails, warn about it and try again
* but this time using EXTRACT_CUNESCAPE_RELAX to keep the
* backslashes verbatim in invalid escape sequences. */
const char *save;
int r;
save = *p;
r = extract_first_word(p, ret, separators, flags);
if (r >= 0)
return r;
if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
/* Retry it with EXTRACT_CUNESCAPE_RELAX. */
*p = save;
r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
if (r >= 0) {
/* It worked this time, hence it must have been an invalid escape sequence. */
log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret);
return r;
}
/* If it's still EINVAL; then it must be unbalanced quoting, report this. */
if (r == -EINVAL)
return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue);
}
/* Can be any error, report it */
return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue);
}
/* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing
* an object that undergoes default argument promotion as an argument to va_start).
* Let's make sure that ExtractFlags fits into an unsigned int. */
assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned));
int extract_many_words(const char **p, const char *separators, unsigned flags, ...) {
va_list ap;
char **l;
int n = 0, i, c, r;
/* Parses a number of words from a string, stripping any
* quotes if necessary. */
assert(p);
/* Count how many words are expected */
va_start(ap, flags);
for (;;) {
if (!va_arg(ap, char **))
break;
n++;
}
va_end(ap);
if (n <= 0)
return 0;
/* Read all words into a temporary array */
l = newa0(char*, n);
for (c = 0; c < n; c++) {
r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
int j;
for (j = 0; j < c; j++)
free(l[j]);
return r;
}
if (r == 0)
break;
}
/* If we managed to parse all words, return them in the passed
* in parameters */
va_start(ap, flags);
for (i = 0; i < n; i++) {
char **v;
v = va_arg(ap, char **);
assert(v);
*v = l[i];
}
va_end(ap);
return c;
}
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "macro.h"
typedef enum ExtractFlags {
EXTRACT_RELAX = 1,
EXTRACT_CUNESCAPE = 2,
EXTRACT_CUNESCAPE_RELAX = 4,
EXTRACT_QUOTES = 8,
EXTRACT_DONT_COALESCE_SEPARATORS = 16,
EXTRACT_RETAIN_ESCAPE = 32,
} ExtractFlags;
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_;

View File

@@ -0,0 +1,960 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include "alloc-util.h"
#include "copy.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
#include "macro.h"
#include "memfd-util.h"
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "util.h"
#include "tmpfile-util.h"
int close_nointr(int fd) {
assert(fd >= 0);
if (close(fd) >= 0)
return 0;
/*
* Just ignore EINTR; a retry loop is the wrong thing to do on
* Linux.
*
* http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
* https://bugzilla.gnome.org/show_bug.cgi?id=682819
* http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
* https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
*/
if (errno == EINTR)
return 0;
return -errno;
}
int safe_close(int fd) {
/*
* Like close_nointr() but cannot fail. Guarantees errno is
* unchanged. Is a NOP with negative fds passed, and returns
* -1, so that it can be used in this syntax:
*
* fd = safe_close(fd);
*/
if (fd >= 0) {
PROTECT_ERRNO;
/* The kernel might return pretty much any error code
* via close(), but the fd will be closed anyway. The
* only condition we want to check for here is whether
* the fd was invalid at all... */
assert_se(close_nointr(fd) != -EBADF);
}
return -1;
}
void safe_close_pair(int p[]) {
assert(p);
if (p[0] == p[1]) {
/* Special case pairs which use the same fd in both
* directions... */
p[0] = p[1] = safe_close(p[0]);
return;
}
p[0] = safe_close(p[0]);
p[1] = safe_close(p[1]);
}
void close_many(const int fds[], size_t n_fd) {
size_t i;
assert(fds || n_fd <= 0);
for (i = 0; i < n_fd; i++)
safe_close(fds[i]);
}
int fclose_nointr(FILE *f) {
assert(f);
/* Same as close_nointr(), but for fclose() */
if (fclose(f) == 0)
return 0;
if (errno == EINTR)
return 0;
return -errno;
}
FILE* safe_fclose(FILE *f) {
/* Same as safe_close(), but for fclose() */
if (f) {
PROTECT_ERRNO;
assert_se(fclose_nointr(f) != -EBADF);
}
return NULL;
}
DIR* safe_closedir(DIR *d) {
if (d) {
PROTECT_ERRNO;
assert_se(closedir(d) >= 0 || errno != EBADF);
}
return NULL;
}
int fd_nonblock(int fd, bool nonblock) {
int flags, nflags;
assert(fd >= 0);
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
return -errno;
if (nonblock)
nflags = flags | O_NONBLOCK;
else
nflags = flags & ~O_NONBLOCK;
if (nflags == flags)
return 0;
if (fcntl(fd, F_SETFL, nflags) < 0)
return -errno;
return 0;
}
int fd_cloexec(int fd, bool cloexec) {
int flags, nflags;
assert(fd >= 0);
flags = fcntl(fd, F_GETFD, 0);
if (flags < 0)
return -errno;
if (cloexec)
nflags = flags | FD_CLOEXEC;
else
nflags = flags & ~FD_CLOEXEC;
if (nflags == flags)
return 0;
if (fcntl(fd, F_SETFD, nflags) < 0)
return -errno;
return 0;
}
#if 0 /* NM_IGNORED */
_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
size_t i;
assert(n_fdset == 0 || fdset);
for (i = 0; i < n_fdset; i++)
if (fdset[i] == fd)
return true;
return false;
}
int close_all_fds(const int except[], size_t n_except) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
assert(n_except == 0 || except);
d = opendir("/proc/self/fd");
if (!d) {
struct rlimit rl;
int fd, max_fd;
/* When /proc isn't available (for example in chroots) the fallback is brute forcing through the fd
* table */
assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
if (rl.rlim_max == 0)
return -EINVAL;
/* Let's take special care if the resource limit is set to unlimited, or actually larger than the range
* of 'int'. Let's avoid implicit overflows. */
max_fd = (rl.rlim_max == RLIM_INFINITY || rl.rlim_max > INT_MAX) ? INT_MAX : (int) (rl.rlim_max - 1);
for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
int q;
if (fd_in_set(fd, except, n_except))
continue;
q = close_nointr(fd);
if (q < 0 && q != -EBADF && r >= 0)
r = q;
}
return r;
}
FOREACH_DIRENT(de, d, return -errno) {
int fd = -1, q;
if (safe_atoi(de->d_name, &fd) < 0)
/* Let's better ignore this, just in case */
continue;
if (fd < 3)
continue;
if (fd == dirfd(d))
continue;
if (fd_in_set(fd, except, n_except))
continue;
q = close_nointr(fd);
if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
r = q;
}
return r;
}
int same_fd(int a, int b) {
struct stat sta, stb;
pid_t pid;
int r, fa, fb;
assert(a >= 0);
assert(b >= 0);
/* Compares two file descriptors. Note that semantics are
* quite different depending on whether we have kcmp() or we
* don't. If we have kcmp() this will only return true for
* dup()ed file descriptors, but not otherwise. If we don't
* have kcmp() this will also return true for two fds of the same
* file, created by separate open() calls. Since we use this
* call mostly for filtering out duplicates in the fd store
* this difference hopefully doesn't matter too much. */
if (a == b)
return true;
/* Try to use kcmp() if we have it. */
pid = getpid_cached();
r = kcmp(pid, pid, KCMP_FILE, a, b);
if (r == 0)
return true;
if (r > 0)
return false;
if (!IN_SET(errno, ENOSYS, EACCES, EPERM))
return -errno;
/* We don't have kcmp(), use fstat() instead. */
if (fstat(a, &sta) < 0)
return -errno;
if (fstat(b, &stb) < 0)
return -errno;
if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
return false;
/* We consider all device fds different, since two device fds
* might refer to quite different device contexts even though
* they share the same inode and backing dev_t. */
if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
return false;
if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
return false;
/* The fds refer to the same inode on disk, let's also check
* if they have the same fd flags. This is useful to
* distinguish the read and write side of a pipe created with
* pipe(). */
fa = fcntl(a, F_GETFL);
if (fa < 0)
return -errno;
fb = fcntl(b, F_GETFL);
if (fb < 0)
return -errno;
return fa == fb;
}
#endif /* NM_IGNORED */
void cmsg_close_all(struct msghdr *mh) {
struct cmsghdr *cmsg;
assert(mh);
CMSG_FOREACH(cmsg, mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
}
bool fdname_is_valid(const char *s) {
const char *p;
/* Validates a name for $LISTEN_FDNAMES. We basically allow
* everything ASCII that's not a control character. Also, as
* special exception the ":" character is not allowed, as we
* use that as field separator in $LISTEN_FDNAMES.
*
* Note that the empty string is explicitly allowed
* here. However, we limit the length of the names to 255
* characters. */
if (!s)
return false;
for (p = s; *p; p++) {
if (*p < ' ')
return false;
if (*p >= 127)
return false;
if (*p == ':')
return false;
}
return p - s < 256;
}
int fd_get_path(int fd, char **ret) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int r;
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
r = readlink_malloc(procfs_path, ret);
if (r == -ENOENT) {
/* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
* things debuggable and distuingish the two. */
if (access("/proc/self/fd/", F_OK) < 0)
/* /proc is not available or not set up properly, we're most likely in some chroot
* environment. */
return errno == ENOENT ? -EOPNOTSUPP : -errno;
return -EBADF; /* The directory exists, hence it's the fd that doesn't. */
}
return r;
}
#if 0 /* NM_IGNORED */
int move_fd(int from, int to, int cloexec) {
int r;
/* Move fd 'from' to 'to', make sure FD_CLOEXEC remains equal if requested, and release the old fd. If
* 'cloexec' is passed as -1, the original FD_CLOEXEC is inherited for the new fd. If it is 0, it is turned
* off, if it is > 0 it is turned on. */
if (from < 0)
return -EBADF;
if (to < 0)
return -EBADF;
if (from == to) {
if (cloexec >= 0) {
r = fd_cloexec(to, cloexec);
if (r < 0)
return r;
}
return to;
}
if (cloexec < 0) {
int fl;
fl = fcntl(from, F_GETFD, 0);
if (fl < 0)
return -errno;
cloexec = !!(fl & FD_CLOEXEC);
}
r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
if (r < 0)
return -errno;
assert(r == to);
safe_close(from);
return to;
}
int acquire_data_fd(const void *data, size_t size, unsigned flags) {
_cleanup_close_pair_ int pipefds[2] = { -1, -1 };
char pattern[] = "/dev/shm/data-fd-XXXXXX";
_cleanup_close_ int fd = -1;
int isz = 0, r;
ssize_t n;
off_t f;
assert(data || size == 0);
/* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
* complex than I wish it was. But here's why:
*
* a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
* read-only. Unfortunately they require kernel 3.17, and at the time of writing we still support 3.14.
*
* b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
* a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
* clients can only bump their size to a system-wide limit, which might be quite low.
*
* c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
* earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
* /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
*
* d) Finally, we try creating a regular file in /dev/shm, which we then delete.
*
* It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
* figure. */
if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
/* As a special case, return /dev/null if we have been called for an empty data block */
r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (r < 0)
return -errno;
return r;
}
if ((flags & ACQUIRE_NO_MEMFD) == 0) {
fd = memfd_new("data-fd");
if (fd < 0)
goto try_pipe;
n = write(fd, data, size);
if (n < 0)
return -errno;
if ((size_t) n != size)
return -EIO;
f = lseek(fd, 0, SEEK_SET);
if (f != 0)
return -errno;
r = memfd_set_sealed(fd);
if (r < 0)
return r;
return TAKE_FD(fd);
}
try_pipe:
if ((flags & ACQUIRE_NO_PIPE) == 0) {
if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
return -errno;
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
if (isz < 0)
return -errno;
if ((size_t) isz < size) {
isz = (int) size;
if (isz < 0 || (size_t) isz != size)
return -E2BIG;
/* Try to bump the pipe size */
(void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
/* See if that worked */
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
if (isz < 0)
return -errno;
if ((size_t) isz < size)
goto try_dev_shm;
}
n = write(pipefds[1], data, size);
if (n < 0)
return -errno;
if ((size_t) n != size)
return -EIO;
(void) fd_nonblock(pipefds[0], false);
return TAKE_FD(pipefds[0]);
}
try_dev_shm:
if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
if (fd < 0)
goto try_dev_shm_without_o_tmpfile;
n = write(fd, data, size);
if (n < 0)
return -errno;
if ((size_t) n != size)
return -EIO;
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
return fd_reopen(fd, O_RDONLY|O_CLOEXEC);
}
try_dev_shm_without_o_tmpfile:
if ((flags & ACQUIRE_NO_REGULAR) == 0) {
fd = mkostemp_safe(pattern);
if (fd < 0)
return fd;
n = write(fd, data, size);
if (n < 0) {
r = -errno;
goto unlink_and_return;
}
if ((size_t) n != size) {
r = -EIO;
goto unlink_and_return;
}
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
r = open(pattern, O_RDONLY|O_CLOEXEC);
if (r < 0)
r = -errno;
unlink_and_return:
(void) unlink(pattern);
return r;
}
return -EOPNOTSUPP;
}
/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
#define DATA_FD_MEMORY_LIMIT (64U*1024U)
/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */
#define DATA_FD_TMP_LIMIT (1024U*1024U)
int fd_duplicate_data_fd(int fd) {
_cleanup_close_ int copy_fd = -1, tmp_fd = -1;
_cleanup_free_ void *remains = NULL;
size_t remains_size = 0;
const char *td;
struct stat st;
int r;
/* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but
* independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be
* somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported
* uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in
* /var/tmp. */
if (fstat(fd, &st) < 0)
return -errno;
/* For now, let's only accept regular files, sockets, pipes and char devices */
if (S_ISDIR(st.st_mode))
return -EISDIR;
if (S_ISLNK(st.st_mode))
return -ELOOP;
if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode))
return -EBADFD;
/* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note
* that we use the reported regular file size only as a hint, given that there are plenty special files in
* /proc and /sys which report a zero file size but can be read from. */
if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) {
/* Try a memfd first */
copy_fd = memfd_new("data-fd");
if (copy_fd >= 0) {
off_t f;
r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0);
if (r < 0)
return r;
f = lseek(copy_fd, 0, SEEK_SET);
if (f != 0)
return -errno;
if (r == 0) {
/* Did it fit into the limit? If so, we are done. */
r = memfd_set_sealed(copy_fd);
if (r < 0)
return r;
return TAKE_FD(copy_fd);
}
/* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */
} else {
_cleanup_(close_pairp) int pipefds[2] = { -1, -1 };
int isz;
/* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather
* then block indefinitely when we hit the pipe size limit */
if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
return -errno;
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
if (isz < 0)
return -errno;
/* Try to enlarge the pipe size if necessary */
if ((size_t) isz < DATA_FD_MEMORY_LIMIT) {
(void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT);
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
if (isz < 0)
return -errno;
}
if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
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
* tell us. Hence, treat this as reason to fall back, just to be
* sure. */
if (r == 0) {
/* Everything fit in, yay! */
(void) fd_nonblock(pipefds[0], false);
return TAKE_FD(pipefds[0]);
}
/* Things didn't fit in. But we read data into the pipe, let's remember that, so that
* when writing the new file we incorporate this first. */
copy_fd = TAKE_FD(pipefds[0]);
}
}
}
/* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */
if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) &&
(DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) {
off_t f;
tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC);
if (tmp_fd < 0)
return tmp_fd;
if (copy_fd >= 0) {
/* If we tried a memfd/pipe first and it ended up being too large, then copy this into the
* temporary file first. */
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0);
if (r < 0)
return r;
assert(r == 0);
}
if (remains_size > 0) {
/* If there were remaining bytes (i.e. read into memory, but not written out yet) from the
* failed copy operation, let's flush them out next. */
r = loop_write(tmp_fd, remains, remains_size, false);
if (r < 0)
return r;
}
r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK);
if (r < 0)
return r;
if (r == 0)
goto finish; /* Yay, it fit in */
/* It didn't fit in. Let's not forget to use what we already used */
f = lseek(tmp_fd, 0, SEEK_SET);
if (f != 0)
return -errno;
safe_close(copy_fd);
copy_fd = TAKE_FD(tmp_fd);
remains = mfree(remains);
remains_size = 0;
}
/* As last fallback use /var/tmp */
r = var_tmp_dir(&td);
if (r < 0)
return r;
tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC);
if (tmp_fd < 0)
return tmp_fd;
if (copy_fd >= 0) {
/* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this
* into the temporary file first. */
r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
if (r < 0)
return r;
assert(r == 0);
}
if (remains_size > 0) {
/* Then, copy in any read but not yet written bytes. */
r = loop_write(tmp_fd, remains, remains_size, false);
if (r < 0)
return r;
}
/* Copy in the rest */
r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
if (r < 0)
return r;
assert(r == 0);
finish:
/* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the
* file again */
return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
}
#endif /* NM_IGNORED */
int fd_move_above_stdio(int fd) {
int flags, copy;
PROTECT_ERRNO;
/* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of
* stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is
* returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that
* might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as
* stdin/stdout/stderr of unrelated code.
*
* Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by
* buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has
* been closed before.
*
* This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an
* error we simply return the original file descriptor, and we do not touch errno. */
if (fd < 0 || fd > 2)
return fd;
flags = fcntl(fd, F_GETFD, 0);
if (flags < 0)
return fd;
if (flags & FD_CLOEXEC)
copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
else
copy = fcntl(fd, F_DUPFD, 3);
if (copy < 0)
return fd;
assert(copy > 2);
(void) close(fd);
return copy;
}
#if 0 /* NM_IGNORED */
int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) {
int fd[3] = { /* Put together an array of fds we work on */
original_input_fd,
original_output_fd,
original_error_fd
};
int r, i,
null_fd = -1, /* if we open /dev/null, we store the fd to it here */
copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */
bool null_readable, null_writable;
/* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is
* specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as
* itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be
* on.
*
* Note that if any of the passed file descriptors are > 2 they will be closed — both on success and on
* failure! Thus, callers should assume that when this function returns the input fds are invalidated.
*
* Note that when this function fails stdin/stdout/stderr might remain half set up!
*
* O_CLOEXEC is turned off for all three file descriptors (which is how it should be for
* stdin/stdout/stderr). */
null_readable = original_input_fd < 0;
null_writable = original_output_fd < 0 || original_error_fd < 0;
/* First step, open /dev/null once, if we need it */
if (null_readable || null_writable) {
/* Let's open this with O_CLOEXEC first, and convert it to non-O_CLOEXEC when we move the fd to the final position. */
null_fd = open("/dev/null", (null_readable && null_writable ? O_RDWR :
null_readable ? O_RDONLY : O_WRONLY) | O_CLOEXEC);
if (null_fd < 0) {
r = -errno;
goto finish;
}
/* If this fd is in the 0…2 range, let's move it out of it */
if (null_fd < 3) {
int copy;
copy = fcntl(null_fd, F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */
if (copy < 0) {
r = -errno;
goto finish;
}
safe_close(null_fd);
null_fd = copy;
}
}
/* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */
for (i = 0; i < 3; i++) {
if (fd[i] < 0)
fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */
else if (fd[i] != i && fd[i] < 3) {
/* This fd is in the 0…2 territory, but not at its intended place, move it out of there, so that we can work there. */
copy_fd[i] = fcntl(fd[i], F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */
if (copy_fd[i] < 0) {
r = -errno;
goto finish;
}
fd[i] = copy_fd[i];
}
}
/* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we
* have freedom to move them around. If the fds already were at the right places then the specific fds are
* -1. Let's now move them to the right places. This is the point of no return. */
for (i = 0; i < 3; i++) {
if (fd[i] == i) {
/* fd is already in place, but let's make sure O_CLOEXEC is off */
r = fd_cloexec(i, false);
if (r < 0)
goto finish;
} else {
assert(fd[i] > 2);
if (dup2(fd[i], i) < 0) { /* Turns off O_CLOEXEC on the new fd. */
r = -errno;
goto finish;
}
}
}
r = 0;
finish:
/* Close the original fds, but only if they were outside of the stdio range. Also, properly check for the same
* fd passed in multiple times. */
safe_close_above_stdio(original_input_fd);
if (original_output_fd != original_input_fd)
safe_close_above_stdio(original_output_fd);
if (original_error_fd != original_input_fd && original_error_fd != original_output_fd)
safe_close_above_stdio(original_error_fd);
/* Close the copies we moved > 2 */
for (i = 0; i < 3; i++)
safe_close(copy_fd[i]);
/* Close our null fd, if it's > 2 */
safe_close_above_stdio(null_fd);
return r;
}
int fd_reopen(int fd, int flags) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int new_fd;
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
* turn O_RDWR fds into O_RDONLY fds.
*
* This doesn't work on sockets (since they cannot be open()ed, ever).
*
* This implicitly resets the file read index to 0. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
new_fd = open(procfs_path, flags);
if (new_fd < 0)
return -errno;
return new_fd;
}
int read_nr_open(void) {
_cleanup_free_ char *nr_open = NULL;
int r;
/* Returns the kernel's current fd limit, either by reading it of /proc/sys if that works, or using the
* hard-coded default compiled-in value of current kernels (1M) if not. This call will never fail. */
r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open);
if (r < 0)
log_debug_errno(r, "Failed to read /proc/sys/fs/nr_open, ignoring: %m");
else {
int v;
r = safe_atoi(nr_open, &v);
if (r < 0)
log_debug_errno(r, "Failed to parse /proc/sys/fs/nr_open value '%s', ignoring: %m", nr_open);
else
return v;
}
/* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */
return 1024 * 1024;
}
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,110 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <dirent.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/socket.h>
#include "macro.h"
/* Make sure we can distinguish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
int close_nointr(int fd);
int safe_close(int fd);
void safe_close_pair(int p[]);
static inline int safe_close_above_stdio(int fd) {
if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -1 */
return -1;
return safe_close(fd);
}
void close_many(const int fds[], size_t n_fd);
int fclose_nointr(FILE *f);
FILE* safe_fclose(FILE *f);
DIR* safe_closedir(DIR *f);
static inline void closep(int *fd) {
safe_close(*fd);
}
static inline void close_pairp(int (*p)[2]) {
safe_close_pair(*p);
}
static inline void fclosep(FILE **f) {
safe_fclose(*f);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
#define _cleanup_close_ _cleanup_(closep)
#define _cleanup_fclose_ _cleanup_(fclosep)
#define _cleanup_pclose_ _cleanup_(pclosep)
#define _cleanup_closedir_ _cleanup_(closedirp)
#define _cleanup_close_pair_ _cleanup_(close_pairp)
int fd_nonblock(int fd, bool nonblock);
int fd_cloexec(int fd, bool cloexec);
int close_all_fds(const int except[], size_t n_except);
int same_fd(int a, int b);
void cmsg_close_all(struct msghdr *mh);
bool fdname_is_valid(const char *s);
int fd_get_path(int fd, char **ret);
int move_fd(int from, int to, int cloexec);
enum {
ACQUIRE_NO_DEV_NULL = 1 << 0,
ACQUIRE_NO_MEMFD = 1 << 1,
ACQUIRE_NO_PIPE = 1 << 2,
ACQUIRE_NO_TMPFILE = 1 << 3,
ACQUIRE_NO_REGULAR = 1 << 4,
};
int acquire_data_fd(const void *data, size_t size, unsigned flags);
int fd_duplicate_data_fd(int fd);
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
/* The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases.
* See the icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources */
#define ERRNO_IS_DISCONNECT(r) \
IN_SET(r, \
ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, \
ENETUNREACH, EHOSTUNREACH, ENOPROTOOPT, EHOSTDOWN, ENONET)
/* Resource exhaustion, could be our fault or general system trouble */
#define ERRNO_IS_RESOURCE(r) \
IN_SET(r, ENOMEM, EMFILE, ENFILE)
int fd_move_above_stdio(int fd);
int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd);
static inline int make_null_stdio(void) {
return rearrange_stdio(-1, -1, -1);
}
/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */
#define TAKE_FD(fd) \
({ \
int _fd_ = (fd); \
(fd) = -1; \
_fd_; \
})
int fd_reopen(int fd, int flags);
int read_nr_open(void);

View File

@@ -0,0 +1,834 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "tmpfile-util.h"
#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
#if 0 /* NM_IGNORED */
int write_string_stream_ts(
FILE *f,
const char *line,
WriteStringFileFlags flags,
struct timespec *ts) {
bool needs_nl;
int r;
assert(f);
assert(line);
if (ferror(f))
return -EIO;
needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
/* If STDIO buffering was disabled, then let's append the newline character to the string itself, so
* that the write goes out in one go, instead of two */
line = strjoina(line, "\n");
needs_nl = false;
}
if (fputs(line, f) == EOF)
return -errno;
if (needs_nl)
if (fputc('\n', f) == EOF)
return -errno;
if (flags & WRITE_STRING_FILE_SYNC)
r = fflush_sync_and_check(f);
else
r = fflush_and_check(f);
if (r < 0)
return r;
if (ts) {
struct timespec twice[2] = {*ts, *ts};
if (futimens(fileno(f), twice) < 0)
return -errno;
}
return 0;
}
static int write_string_file_atomic(
const char *fn,
const char *line,
WriteStringFileFlags flags,
struct timespec *ts) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
int r;
assert(fn);
assert(line);
r = fopen_temporary(fn, &f, &p);
if (r < 0)
return r;
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
(void) fchmod_umask(fileno(f), 0644);
r = write_string_stream_ts(f, line, flags, ts);
if (r < 0)
goto fail;
if (rename(p, fn) < 0) {
r = -errno;
goto fail;
}
return 0;
fail:
(void) unlink(p);
return r;
}
int write_string_file_ts(
const char *fn,
const char *line,
WriteStringFileFlags flags,
struct timespec *ts) {
_cleanup_fclose_ FILE *f = NULL;
int q, r;
assert(fn);
assert(line);
/* We don't know how to verify whether the file contents was already on-disk. */
assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC)));
if (flags & WRITE_STRING_FILE_ATOMIC) {
assert(flags & WRITE_STRING_FILE_CREATE);
r = write_string_file_atomic(fn, line, flags, ts);
if (r < 0)
goto fail;
return r;
} else
assert(!ts);
if (flags & WRITE_STRING_FILE_CREATE) {
f = fopen(fn, "we");
if (!f) {
r = -errno;
goto fail;
}
} else {
int fd;
/* We manually build our own version of fopen(..., "we") that
* works without O_CREAT */
fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY | ((flags & WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0));
if (fd < 0) {
r = -errno;
goto fail;
}
f = fdopen(fd, "w");
if (!f) {
r = -errno;
safe_close(fd);
goto fail;
}
}
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
if (flags & WRITE_STRING_FILE_DISABLE_BUFFER)
setvbuf(f, NULL, _IONBF, 0);
r = write_string_stream_ts(f, line, flags, ts);
if (r < 0)
goto fail;
return 0;
fail:
if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE))
return r;
f = safe_fclose(f);
/* OK, the operation failed, but let's see if the right
* contents in place already. If so, eat up the error. */
q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
if (q <= 0)
return r;
return 0;
}
int write_string_filef(
const char *fn,
WriteStringFileFlags flags,
const char *format, ...) {
_cleanup_free_ char *p = NULL;
va_list ap;
int r;
va_start(ap, format);
r = vasprintf(&p, format, ap);
va_end(ap);
if (r < 0)
return -ENOMEM;
return write_string_file(fn, p, flags);
}
int read_one_line_file(const char *fn, char **line) {
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(fn);
assert(line);
f = fopen(fn, "re");
if (!f)
return -errno;
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
r = read_line(f, LONG_LINE_MAX, line);
return r < 0 ? r : 0;
}
int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *buf = NULL;
size_t l, k;
assert(fn);
assert(blob);
l = strlen(blob);
if (accept_extra_nl && endswith(blob, "\n"))
accept_extra_nl = false;
buf = malloc(l + accept_extra_nl + 1);
if (!buf)
return -ENOMEM;
f = fopen(fn, "re");
if (!f)
return -errno;
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
/* We try to read one byte more than we need, so that we know whether we hit eof */
errno = 0;
k = fread(buf, 1, l + accept_extra_nl + 1, f);
if (ferror(f))
return errno > 0 ? -errno : -EIO;
if (k != l && k != l + accept_extra_nl)
return 0;
if (memcmp(buf, blob, l) != 0)
return 0;
if (k > l && buf[l] != '\n')
return 0;
return 1;
}
#endif /* NM_IGNORED */
int read_full_stream(
FILE *f,
char **ret_contents,
size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
struct stat st;
size_t n, l;
int fd;
assert(f);
assert(ret_contents);
n = LINE_MAX; /* Start size */
fd = fileno(f);
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
* optimize our buffering) */
if (fstat(fileno(f), &st) < 0)
return -errno;
if (S_ISREG(st.st_mode)) {
/* Safety check */
if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
/* Start with the right file size, but be prepared for files from /proc which generally report a file
* size of 0. Note that we increase the size to read here by one, so that the first read attempt
* already makes us notice the EOF. */
if (st.st_size > 0)
n = st.st_size + 1;
}
}
l = 0;
for (;;) {
char *t;
size_t k;
t = realloc(buf, n + 1);
if (!t)
return -ENOMEM;
buf = t;
errno = 0;
k = fread(buf + l, 1, n - l, f);
if (k > 0)
l += k;
if (ferror(f))
return errno > 0 ? -errno : -EIO;
if (feof(f))
break;
/* We aren't expecting fread() to return a short read outside
* of (error && eof), assert buffer is full and enlarge buffer.
*/
assert(l == n);
/* Safety check */
if (n >= READ_FULL_BYTES_MAX)
return -E2BIG;
n = MIN(n * 2, READ_FULL_BYTES_MAX);
}
if (!ret_size) {
/* Safety check: if the caller doesn't want to know the size of what we just read it will rely on the
* trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise
* there'd be ambiguity about what we just read. */
if (memchr(buf, 0, l))
return -EBADMSG;
}
buf[l] = 0;
*ret_contents = TAKE_PTR(buf);
if (ret_size)
*ret_size = l;
return 0;
}
int read_full_file(const char *fn, char **contents, size_t *size) {
_cleanup_fclose_ FILE *f = NULL;
assert(fn);
assert(contents);
f = fopen(fn, "re");
if (!f)
return -errno;
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
return read_full_stream(f, contents, size);
}
#if 0 /* NM_IGNORED */
int executable_is_script(const char *path, char **interpreter) {
_cleanup_free_ char *line = NULL;
size_t len;
char *ans;
int r;
assert(path);
r = read_one_line_file(path, &line);
if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */
return 0;
if (r < 0)
return r;
if (!startswith(line, "#!"))
return 0;
ans = strstrip(line + 2);
len = strcspn(ans, " \t");
if (len == 0)
return 0;
ans = strndup(ans, len);
if (!ans)
return -ENOMEM;
*interpreter = ans;
return 1;
}
#endif /* NM_IGNORED */
/**
* Retrieve one field from a file like /proc/self/status. pattern
* should not include whitespace or the delimiter (':'). pattern matches only
* the beginning of a line. Whitespace before ':' is skipped. Whitespace and
* zeros after the ':' will be skipped. field must be freed afterwards.
* terminator specifies the terminating characters of the field value (not
* included in the value).
*/
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
_cleanup_free_ char *status = NULL;
char *t, *f;
size_t len;
int r;
assert(terminator);
assert(filename);
assert(pattern);
assert(field);
r = read_full_file(filename, &status, NULL);
if (r < 0)
return r;
t = status;
do {
bool pattern_ok;
do {
t = strstr(t, pattern);
if (!t)
return -ENOENT;
/* Check that pattern occurs in beginning of line. */
pattern_ok = (t == status || t[-1] == '\n');
t += strlen(pattern);
} while (!pattern_ok);
t += strspn(t, " \t");
if (!*t)
return -ENOENT;
} while (*t != ':');
t++;
if (*t) {
t += strspn(t, " \t");
/* Also skip zeros, because when this is used for
* capabilities, we don't want the zeros. This way the
* same capability set always maps to the same string,
* irrespective of the total capability set size. For
* other numbers it shouldn't matter. */
t += strspn(t, "0");
/* Back off one char if there's nothing but whitespace
and zeros */
if (!*t || isspace(*t))
t--;
}
len = strcspn(t, terminator);
f = strndup(t, len);
if (!f)
return -ENOMEM;
*field = f;
return 0;
}
DIR *xopendirat(int fd, const char *name, int flags) {
int nfd;
DIR *d;
assert(!(flags & O_CREAT));
nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
if (nfd < 0)
return NULL;
d = fdopendir(nfd);
if (!d) {
safe_close(nfd);
return NULL;
}
return d;
}
#if 0 /* NM_IGNORED */
static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
char **i;
assert(path);
assert(mode);
assert(_f);
if (!path_strv_resolve_uniq(search, root))
return -ENOMEM;
STRV_FOREACH(i, search) {
_cleanup_free_ char *p = NULL;
FILE *f;
if (root)
p = strjoin(root, *i, "/", path);
else
p = strjoin(*i, "/", path);
if (!p)
return -ENOMEM;
f = fopen(p, mode);
if (f) {
*_f = f;
return 0;
}
if (errno != ENOENT)
return -errno;
}
return -ENOENT;
}
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
_cleanup_strv_free_ char **copy = NULL;
assert(path);
assert(mode);
assert(_f);
if (path_is_absolute(path)) {
FILE *f;
f = fopen(path, mode);
if (f) {
*_f = f;
return 0;
}
return -errno;
}
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
return search_and_fopen_internal(path, mode, root, copy, _f);
}
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
_cleanup_strv_free_ char **s = NULL;
if (path_is_absolute(path)) {
FILE *f;
f = fopen(path, mode);
if (f) {
*_f = f;
return 0;
}
return -errno;
}
s = strv_split_nulstr(search);
if (!s)
return -ENOMEM;
return search_and_fopen_internal(path, mode, root, s, _f);
}
#endif /* NM_IGNORED */
int fflush_and_check(FILE *f) {
assert(f);
errno = 0;
fflush(f);
if (ferror(f))
return errno > 0 ? -errno : -EIO;
return 0;
}
#if 0 /* NM_IGNORED */
int fflush_sync_and_check(FILE *f) {
int r;
assert(f);
r = fflush_and_check(f);
if (r < 0)
return r;
if (fsync(fileno(f)) < 0)
return -errno;
r = fsync_directory_of_file(fileno(f));
if (r < 0)
return r;
return 0;
}
int write_timestamp_file_atomic(const char *fn, usec_t n) {
char ln[DECIMAL_STR_MAX(n)+2];
/* Creates a "timestamp" file, that contains nothing but a
* usec_t timestamp, formatted in ASCII. */
if (n <= 0 || n >= USEC_INFINITY)
return -ERANGE;
xsprintf(ln, USEC_FMT "\n", n);
return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
}
int read_timestamp_file(const char *fn, usec_t *ret) {
_cleanup_free_ char *ln = NULL;
uint64_t t;
int r;
r = read_one_line_file(fn, &ln);
if (r < 0)
return r;
r = safe_atou64(ln, &t);
if (r < 0)
return r;
if (t <= 0 || t >= (uint64_t) USEC_INFINITY)
return -ERANGE;
*ret = (usec_t) t;
return 0;
}
#endif /* NM_IGNORED */
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
int r;
assert(s);
/* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
* when specified shall initially point to a boolean variable initialized to false. It is set to true after the
* first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
* element, but not before the first one. */
if (!f)
f = stdout;
if (space) {
if (!separator)
separator = " ";
if (*space) {
r = fputs(separator, f);
if (r < 0)
return r;
}
*space = true;
}
return fputs(s, f);
}
#if 0 /* NM_IGNORED */
/* A bitmask of the EOL markers we know */
typedef enum EndOfLineMarker {
EOL_NONE = 0,
EOL_ZERO = 1 << 0, /* \0 (aka NUL) */
EOL_TEN = 1 << 1, /* \n (aka NL, aka LF) */
EOL_THIRTEEN = 1 << 2, /* \r (aka CR) */
} EndOfLineMarker;
static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) {
if (!IN_SET(flags, READ_LINE_ONLY_NUL)) {
if (c == '\n')
return EOL_TEN;
if (c == '\r')
return EOL_THIRTEEN;
}
if (c == '\0')
return EOL_ZERO;
return EOL_NONE;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile);
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
size_t n = 0, allocated = 0, count = 0;
_cleanup_free_ char *buffer = NULL;
int r;
assert(f);
/* Something like a bounded version of getline().
*
* Considers EOF, \n, \r and \0 end of line delimiters (or combinations of these), and does not include these
* delimiters in the string returned. Specifically, recognizes the following combinations of markers as line
* endings:
*
* • \n (UNIX)
* • \r (old MacOS)
* • \0 (C strings)
* • \n\0
* • \r\0
* • \r\n (Windows)
* • \n\r
* • \r\n\0
* • \n\r\0
*
* Returns the number of bytes read from the files (i.e. including delimiters — this hence usually differs from
* the number of characters in the returned string). When EOF is hit, 0 is returned.
*
* The input parameter limit is the maximum numbers of characters in the returned string, i.e. excluding
* delimiters. If the limit is hit we fail and return -ENOBUFS.
*
* If a line shall be skipped ret may be initialized as NULL. */
if (ret) {
if (!GREEDY_REALLOC(buffer, allocated, 1))
return -ENOMEM;
}
{
_unused_ _cleanup_(funlockfilep) FILE *flocked = f;
EndOfLineMarker previous_eol = EOL_NONE;
flockfile(f);
for (;;) {
EndOfLineMarker eol;
char c;
if (n >= limit)
return -ENOBUFS;
if (count >= INT_MAX) /* We couldn't return the counter anymore as "int", hence refuse this */
return -ENOBUFS;
r = safe_fgetc(f, &c);
if (r < 0)
return r;
if (r == 0) /* EOF is definitely EOL */
break;
eol = categorize_eol(c, flags);
if (FLAGS_SET(previous_eol, EOL_ZERO) ||
(eol == EOL_NONE && previous_eol != EOL_NONE) ||
(eol != EOL_NONE && (previous_eol & eol) != 0)) {
/* Previous char was a NUL? This is not an EOL, but the previous char was? This type of
* EOL marker has been seen right before? In either of these three cases we are
* done. But first, let's put this character back in the queue. (Note that we have to
* cast this to (unsigned char) here as ungetc() expects a positive 'int', and if we
* are on an architecture where 'char' equals 'signed char' we need to ensure we don't
* pass a negative value here. That said, to complicate things further ungetc() is
* actually happy with most negative characters and implicitly casts them back to
* positive ones as needed, except for \xff (aka -1, aka EOF), which it refuses. What a
* godawful API!) */
assert_se(ungetc((unsigned char) c, f) != EOF);
break;
}
count++;
if (eol != EOL_NONE) {
previous_eol |= eol;
continue;
}
if (ret) {
if (!GREEDY_REALLOC(buffer, allocated, n + 2))
return -ENOMEM;
buffer[n] = c;
}
n++;
}
}
if (ret) {
buffer[n] = 0;
*ret = TAKE_PTR(buffer);
}
return (int) count;
}
int safe_fgetc(FILE *f, char *ret) {
int k;
assert(f);
/* A safer version of plain fgetc(): let's propagate the error that happened while reading as such, and
* separate the EOF condition from the byte read, to avoid those confusion signed/unsigned issues fgetc()
* has. */
errno = 0;
k = fgetc(f);
if (k == EOF) {
if (ferror(f))
return errno > 0 ? -errno : -EIO;
if (ret)
*ret = 0;
return 0;
}
if (ret)
*ret = k;
return 1;
}
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <dirent.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include "macro.h"
#include "time-util.h"
#define LONG_LINE_MAX (1U*1024U*1024U)
typedef enum {
WRITE_STRING_FILE_CREATE = 1 << 0,
WRITE_STRING_FILE_ATOMIC = 1 << 1,
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 2,
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 3,
WRITE_STRING_FILE_SYNC = 1 << 4,
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5,
WRITE_STRING_FILE_NOFOLLOW = 1 << 6,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
and friends. */
} WriteStringFileFlags;
int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts);
static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) {
return write_string_stream_ts(f, line, flags, NULL);
}
int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts);
static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_ts(fn, line, flags, NULL);
}
int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4);
int read_one_line_file(const char *fn, char **line);
int read_full_file(const char *fn, char **contents, size_t *size);
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 executable_is_script(const char *path, char **interpreter);
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
DIR *xopendirat(int dirfd, const char *name, int flags);
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f);
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);
typedef enum ReadLineFlags {
READ_LINE_ONLY_NUL = 1 << 0,
} ReadLineFlags;
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret);
static inline int read_line(FILE *f, size_t limit, char **ret) {
return read_line_full(f, limit, 0, 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 safe_fgetc(FILE *f, char *ret);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,111 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/inotify.h>
#include <sys/types.h>
#include <unistd.h>
#include "time-util.h"
#include "util.h"
int unlink_noerrno(const char *path);
int rmdir_parents(const char *path, const char *stop);
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
int readlinkat_malloc(int fd, const char *p, char **ret);
int readlink_malloc(const char *p, char **r);
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid);
int fchmod_umask(int fd, mode_t mode);
int fchmod_opath(int fd, mode_t m);
int fd_warn_permissions(const char *path, int fd);
#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
int touch(const char *path);
int symlink_idempotent(const char *from, const char *to, bool make_relative);
int symlink_atomic(const char *from, const char *to);
int mknod_atomic(const char *path, mode_t mode, dev_t dev);
int mkfifo_atomic(const char *path, mode_t mode);
int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode);
int get_files_in_directory(const char *path, char ***list);
int tmp_dir(const char **ret);
int var_tmp_dir(const char **ret);
int unlink_or_warn(const char *filename);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
for ((e) = &buffer.ev; \
(uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
(e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
enum {
CHASE_PREFIX_ROOT = 1 << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
CHASE_NONEXISTENT = 1 << 1, /* If set, it's OK if the path doesn't actually exist. */
CHASE_NO_AUTOFS = 1 << 2, /* If set, return -EREMOTE if autofs mount point found */
CHASE_SAFE = 1 << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
CHASE_OPEN = 1 << 4, /* If set, return an O_PATH object to the final component */
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 */
#define CHASE_SYMLINKS_MAX 32
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path);
int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir);
int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat);
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
static inline void rmdir_and_free(char *p) {
PROTECT_ERRNO;
(void) rmdir(p);
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
static inline void unlink_and_free(char *p) {
(void) unlink_noerrno(p);
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
int access_fd(int fd, int mode);
void unlink_tempfilep(char (*p)[]);
int unlinkat_deallocate(int fd, const char *name, int flags);
int fsync_directory_of_file(int fd);
int fsync_path_at(int at_fd, const char *path);
int open_parent(const char *path, int flags, mode_t mode);

View File

@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <string.h>
#include "hash-funcs.h"
#include "path-util.h"
void string_hash_func(const char *p, struct siphash *state) {
siphash24_compress(p, strlen(p) + 1, state);
}
#if 0 /* NM_IGNORED */
DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func);
void path_hash_func(const char *q, struct siphash *state) {
size_t n;
assert(q);
assert(state);
/* Calculates a hash for a path in a way this duplicate inner slashes don't make a differences, and also
* whether there's a trailing slash or not. This fits well with the semantics of path_compare(), which does
* similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those
* which begin in a slash or not) will hash differently though. */
n = strspn(q, "/");
if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */
siphash24_compress(q, 1, state);
q += n;
}
for (;;) {
/* Determine length of next component */
n = strcspn(q, "/");
if (n == 0) /* Reached the end? */
break;
/* Add this component to the hash and skip over it */
siphash24_compress(q, n, state);
q += n;
/* How many slashes follow this component? */
n = strspn(q, "/");
if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */
break;
/* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */
siphash24_compress(q, 1, state);
q += n;
}
}
int path_compare_func(const char *a, const char *b) {
return path_compare(a, b);
}
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) {
siphash24_compress(&p, sizeof(p), state);
}
int trivial_compare_func(const void *a, const void *b) {
return CMP(a, b);
}
const struct hash_ops trivial_hash_ops = {
.hash = trivial_hash_func,
.compare = trivial_compare_func,
};
void uint64_hash_func(const uint64_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(uint64_t), state);
}
int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
return CMP(*a, *b);
}
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 dev_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(dev_t), state);
}
int devt_compare_func(const dev_t *a, const dev_t *b) {
return CMP(*a, *b);
}
DEFINE_HASH_OPS(devt_hash_ops, dev_t, devt_hash_func, devt_compare_func);
#endif
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,106 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "alloc-util.h"
#include "macro.h"
#include "siphash24.h"
typedef void (*hash_func_t)(const void *p, struct siphash *state);
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;
};
#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 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
* or suchlike. */
void trivial_hash_func(const void *p, struct siphash *state);
int trivial_compare_func(const void *a, const void *b) _const_;
extern const struct hash_ops trivial_hash_ops;
/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
* values indirectly, since they don't fit in a pointer. */
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 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
#define devt_compare_func uint64_compare_func
#define devt_hash_ops uint64_hash_ops
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,430 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include "hash-funcs.h"
#include "macro.h"
#include "util.h"
/*
* A hash table implementation. As a minor optimization a NULL hashmap object
* will be treated as empty hashmap for all read operations. That way it is not
* necessary to instantiate an object for each Hashmap use.
*
* If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
* the implementation will:
* - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
* - perform extra checks for invalid use of iterators
*/
#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
* internal_*() directly). */
typedef struct HashmapBase HashmapBase;
/* Specific hashmap/set types */
typedef struct Hashmap Hashmap; /* Maps keys to values */
typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
typedef struct Set Set; /* Stores just keys */
typedef struct IteratedCache IteratedCache; /* Caches the iterated order of one of the above */
/* Ideally the Iterator would be an opaque struct, but it is instantiated
* by hashmap users, so the definition has to be here. Do not use its fields
* directly. */
typedef struct {
unsigned idx; /* index of an entry to be iterated next */
const void *next_key; /* expected value of that entry's key pointer */
#if ENABLE_DEBUG_HASHMAP
unsigned put_count; /* hashmap's put_count recorded at start of iteration */
unsigned rem_count; /* hashmap's rem_count in previous iteration */
unsigned prev_idx; /* idx in previous iteration */
#endif
} Iterator;
#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
/* Macros for type checking */
#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
(__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
__builtin_types_compatible_p(typeof(h), Hashmap*) || \
__builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \
__builtin_types_compatible_p(typeof(h), Set*))
#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \
(__builtin_types_compatible_p(typeof(h), Hashmap*) || \
__builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \
#define HASHMAP_BASE(h) \
__builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \
(HashmapBase*)(h), \
(void)0)
#define PLAIN_HASHMAP(h) \
__builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \
(Hashmap*)(h), \
(void)0)
#if ENABLE_DEBUG_HASHMAP
# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line
# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__
# define HASHMAP_DEBUG_PASS_ARGS , func, file, line
#else
# define HASHMAP_DEBUG_PARAMS
# define HASHMAP_DEBUG_SRC_ARGS
# define HASHMAP_DEBUG_PASS_ARGS
#endif
Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#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, 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), NULL, NULL);
}
static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
}
static inline Hashmap *hashmap_free_free(Hashmap *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(HASHMAP_BASE(h), NULL, free);
}
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*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
}
IteratedCache *iterated_cache_free(IteratedCache *cache);
int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries);
HashmapBase *internal_hashmap_copy(HashmapBase *h);
static inline Hashmap *hashmap_copy(Hashmap *h) {
return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
}
static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
}
int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h);
static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) {
return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
}
static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) {
return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
}
int hashmap_put(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
return hashmap_put(PLAIN_HASHMAP(h), key, value);
}
int hashmap_update(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
return hashmap_update(PLAIN_HASHMAP(h), key, value);
}
int hashmap_replace(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) {
return hashmap_replace(PLAIN_HASHMAP(h), key, value);
}
void *internal_hashmap_get(HashmapBase *h, const void *key);
static inline void *hashmap_get(Hashmap *h, const void *key) {
return internal_hashmap_get(HASHMAP_BASE(h), key);
}
static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
return internal_hashmap_get(HASHMAP_BASE(h), key);
}
void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) {
return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
}
bool internal_hashmap_contains(HashmapBase *h, const void *key);
static inline bool hashmap_contains(Hashmap *h, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(h), key);
}
static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(h), key);
}
void *internal_hashmap_remove(HashmapBase *h, const void *key);
static inline void *hashmap_remove(Hashmap *h, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(h), key);
}
static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(h), key);
}
void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) {
return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
}
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);
}
int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value);
}
int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
}
/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
* should just work, allow this by having looser type-checking here. */
int internal_hashmap_merge(Hashmap *h, Hashmap *other);
#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
#define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
static inline int hashmap_move(Hashmap *h, Hashmap *other) {
return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
}
static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
}
int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
}
static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
}
unsigned internal_hashmap_size(HashmapBase *h) _pure_;
static inline unsigned hashmap_size(Hashmap *h) {
return internal_hashmap_size(HASHMAP_BASE(h));
}
static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
return internal_hashmap_size(HASHMAP_BASE(h));
}
static inline bool hashmap_isempty(Hashmap *h) {
return hashmap_size(h) == 0;
}
static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
return ordered_hashmap_size(h) == 0;
}
unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
static inline unsigned hashmap_buckets(Hashmap *h) {
return internal_hashmap_buckets(HASHMAP_BASE(h));
}
static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
return internal_hashmap_buckets(HASHMAP_BASE(h));
}
bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key);
static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) {
return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
}
static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) {
return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
}
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), NULL, NULL);
}
static inline void ordered_hashmap_clear(OrderedHashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
}
static inline void hashmap_clear_free(Hashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
}
static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
}
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) {
internal_hashmap_clear(HASHMAP_BASE(h), free, free);
}
/*
* Note about all *_first*() functions
*
* For plain Hashmaps and Sets the order of entries is undefined.
* The functions find whatever entry is first in the implementation
* internal order.
*
* Only for OrderedHashmaps the order is well defined and finding
* the first entry is O(1).
*/
void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key);
static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
}
static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
}
static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
}
static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
}
static inline void *hashmap_steal_first(Hashmap *h) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
}
static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
}
static inline void *hashmap_first(Hashmap *h) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
}
static inline void *ordered_hashmap_first(OrderedHashmap *h) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
}
static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) {
void *key = NULL;
(void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key);
return key;
}
static inline void *hashmap_steal_first_key(Hashmap *h) {
return internal_hashmap_first_key(HASHMAP_BASE(h), true);
}
static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
return internal_hashmap_first_key(HASHMAP_BASE(h), true);
}
static inline void *hashmap_first_key(Hashmap *h) {
return internal_hashmap_first_key(HASHMAP_BASE(h), false);
}
static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
return internal_hashmap_first_key(HASHMAP_BASE(h), false);
}
#define hashmap_clear_with_destructor(_s, _f) \
({ \
void *_item; \
while ((_item = hashmap_steal_first(_s))) \
_f(_item); \
})
#define hashmap_free_with_destructor(_s, _f) \
({ \
hashmap_clear_with_destructor(_s, _f); \
hashmap_free(_s); \
})
#define ordered_hashmap_clear_with_destructor(_s, _f) \
({ \
void *_item; \
while ((_item = ordered_hashmap_steal_first(_s))) \
_f(_item); \
})
#define ordered_hashmap_free_with_destructor(_s, _f) \
({ \
ordered_hashmap_clear_with_destructor(_s, _f); \
ordered_hashmap_free(_s); \
})
/* no hashmap_next */
void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
char **internal_hashmap_get_strv(HashmapBase *h);
static inline char **hashmap_get_strv(Hashmap *h) {
return internal_hashmap_get_strv(HASHMAP_BASE(h));
}
static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
return internal_hashmap_get_strv(HASHMAP_BASE(h));
}
/*
* Hashmaps are iterated in unpredictable order.
* OrderedHashmaps are an exception to this. They are iterated in the order
* the entries were inserted.
* It is safe to remove the current entry.
*/
#define HASHMAP_FOREACH(e, h, i) \
for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); )
#define ORDERED_HASHMAP_FOREACH(e, h, i) \
for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); )
#define HASHMAP_FOREACH_KEY(e, k, h, i) \
for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free);
#define _cleanup_iterated_cache_free_ _cleanup_(iterated_cache_freep)

View File

@@ -0,0 +1,823 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include "alloc-util.h"
#include "hexdecoct.h"
#include "macro.h"
#include "string-util.h"
#include "util.h"
char octchar(int x) {
return '0' + (x & 7);
}
int unoctchar(char c) {
if (c >= '0' && c <= '7')
return c - '0';
return -EINVAL;
}
char decchar(int x) {
return '0' + (x % 10);
}
int undecchar(char c) {
if (c >= '0' && c <= '9')
return c - '0';
return -EINVAL;
}
char hexchar(int x) {
static const char table[16] = "0123456789abcdef";
return table[x & 15];
}
int unhexchar(char c) {
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -EINVAL;
}
char *hexmem(const void *p, size_t l) {
const uint8_t *x;
char *r, *z;
z = r = new(char, l * 2 + 1);
if (!r)
return NULL;
for (x = p; x < (const uint8_t*) p + l; x++) {
*(z++) = hexchar(*x >> 4);
*(z++) = hexchar(*x & 15);
}
*z = 0;
return r;
}
static int unhex_next(const char **p, size_t *l) {
int r;
assert(p);
assert(l);
/* Find the next non-whitespace character, and decode it. We
* greedily skip all preceding and all following whitespace. */
for (;;) {
if (*l == 0)
return -EPIPE;
if (!strchr(WHITESPACE, **p))
break;
/* Skip leading whitespace */
(*p)++, (*l)--;
}
r = unhexchar(**p);
if (r < 0)
return r;
for (;;) {
(*p)++, (*l)--;
if (*l == 0 || !strchr(WHITESPACE, **p))
break;
/* Skip following whitespace */
}
return r;
}
int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) {
_cleanup_free_ uint8_t *buf = NULL;
const char *x;
uint8_t *z;
assert(ret);
assert(ret_len);
assert(p || l == 0);
if (l == (size_t) -1)
l = strlen(p);
/* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */
buf = malloc((l + 1) / 2 + 1);
if (!buf)
return -ENOMEM;
for (x = p, z = buf;;) {
int a, b;
a = unhex_next(&x, &l);
if (a == -EPIPE) /* End of string */
break;
if (a < 0)
return a;
b = unhex_next(&x, &l);
if (b < 0)
return b;
*(z++) = (uint8_t) a << 4 | (uint8_t) b;
}
*z = 0;
*ret_len = (size_t) (z - buf);
*ret = TAKE_PTR(buf);
return 0;
}
#if 0 /* NM_IGNORED */
/* https://tools.ietf.org/html/rfc4648#section-6
* Notice that base32hex differs from base32 in the alphabet it uses.
* The distinction is that the base32hex representation preserves the
* order of the underlying data when compared as bytestrings, this is
* useful when representing NSEC3 hashes, as one can then verify the
* order of hashes directly from their representation. */
char base32hexchar(int x) {
static const char table[32] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUV";
return table[x & 31];
}
int unbase32hexchar(char c) {
unsigned offset;
if (c >= '0' && c <= '9')
return c - '0';
offset = '9' - '0' + 1;
if (c >= 'A' && c <= 'V')
return c - 'A' + offset;
return -EINVAL;
}
char *base32hexmem(const void *p, size_t l, bool padding) {
char *r, *z;
const uint8_t *x;
size_t len;
assert(p || l == 0);
if (padding)
/* five input bytes makes eight output bytes, padding is added so we must round up */
len = 8 * (l + 4) / 5;
else {
/* same, but round down as there is no padding */
len = 8 * l / 5;
switch (l % 5) {
case 4:
len += 7;
break;
case 3:
len += 5;
break;
case 2:
len += 4;
break;
case 1:
len += 2;
break;
}
}
z = r = malloc(len + 1);
if (!r)
return NULL;
for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
* x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
*(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
*(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
*(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
*(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
*(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
*(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */
*(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */
*(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */
}
switch (l % 5) {
case 4:
*(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
*(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
*(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
*(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
*(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
*(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */
*(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */
if (padding)
*(z++) = '=';
break;
case 3:
*(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
*(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
*(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
*(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
*(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */
if (padding) {
*(z++) = '=';
*(z++) = '=';
*(z++) = '=';
}
break;
case 2:
*(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
*(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
*(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
*(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */
if (padding) {
*(z++) = '=';
*(z++) = '=';
*(z++) = '=';
*(z++) = '=';
}
break;
case 1:
*(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
*(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */
if (padding) {
*(z++) = '=';
*(z++) = '=';
*(z++) = '=';
*(z++) = '=';
*(z++) = '=';
*(z++) = '=';
}
break;
}
*z = 0;
return r;
}
int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) {
_cleanup_free_ uint8_t *r = NULL;
int a, b, c, d, e, f, g, h;
uint8_t *z;
const char *x;
size_t len;
unsigned pad = 0;
assert(p || l == 0);
assert(mem);
assert(_len);
if (l == (size_t) -1)
l = strlen(p);
/* padding ensures any base32hex input has input divisible by 8 */
if (padding && l % 8 != 0)
return -EINVAL;
if (padding) {
/* strip the padding */
while (l > 0 && p[l - 1] == '=' && pad < 7) {
pad++;
l--;
}
}
/* a group of eight input bytes needs five output bytes, in case of
* padding we need to add some extra bytes */
len = (l / 8) * 5;
switch (l % 8) {
case 7:
len += 4;
break;
case 5:
len += 3;
break;
case 4:
len += 2;
break;
case 2:
len += 1;
break;
case 0:
break;
default:
return -EINVAL;
}
z = r = malloc(len + 1);
if (!r)
return -ENOMEM;
for (x = p; x < p + (l / 8) * 8; x += 8) {
/* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
* e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
a = unbase32hexchar(x[0]);
if (a < 0)
return -EINVAL;
b = unbase32hexchar(x[1]);
if (b < 0)
return -EINVAL;
c = unbase32hexchar(x[2]);
if (c < 0)
return -EINVAL;
d = unbase32hexchar(x[3]);
if (d < 0)
return -EINVAL;
e = unbase32hexchar(x[4]);
if (e < 0)
return -EINVAL;
f = unbase32hexchar(x[5]);
if (f < 0)
return -EINVAL;
g = unbase32hexchar(x[6]);
if (g < 0)
return -EINVAL;
h = unbase32hexchar(x[7]);
if (h < 0)
return -EINVAL;
*(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
*(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
*(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
*(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
*(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */
}
switch (l % 8) {
case 7:
a = unbase32hexchar(x[0]);
if (a < 0)
return -EINVAL;
b = unbase32hexchar(x[1]);
if (b < 0)
return -EINVAL;
c = unbase32hexchar(x[2]);
if (c < 0)
return -EINVAL;
d = unbase32hexchar(x[3]);
if (d < 0)
return -EINVAL;
e = unbase32hexchar(x[4]);
if (e < 0)
return -EINVAL;
f = unbase32hexchar(x[5]);
if (f < 0)
return -EINVAL;
g = unbase32hexchar(x[6]);
if (g < 0)
return -EINVAL;
/* g == 000VV000 */
if (g & 7)
return -EINVAL;
*(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
*(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
*(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
*(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
break;
case 5:
a = unbase32hexchar(x[0]);
if (a < 0)
return -EINVAL;
b = unbase32hexchar(x[1]);
if (b < 0)
return -EINVAL;
c = unbase32hexchar(x[2]);
if (c < 0)
return -EINVAL;
d = unbase32hexchar(x[3]);
if (d < 0)
return -EINVAL;
e = unbase32hexchar(x[4]);
if (e < 0)
return -EINVAL;
/* e == 000SSSS0 */
if (e & 1)
return -EINVAL;
*(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
*(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
*(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
break;
case 4:
a = unbase32hexchar(x[0]);
if (a < 0)
return -EINVAL;
b = unbase32hexchar(x[1]);
if (b < 0)
return -EINVAL;
c = unbase32hexchar(x[2]);
if (c < 0)
return -EINVAL;
d = unbase32hexchar(x[3]);
if (d < 0)
return -EINVAL;
/* d == 000W0000 */
if (d & 15)
return -EINVAL;
*(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
*(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
break;
case 2:
a = unbase32hexchar(x[0]);
if (a < 0)
return -EINVAL;
b = unbase32hexchar(x[1]);
if (b < 0)
return -EINVAL;
/* b == 000YYY00 */
if (b & 3)
return -EINVAL;
*(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
break;
case 0:
break;
default:
return -EINVAL;
}
*z = 0;
*mem = TAKE_PTR(r);
*_len = len;
return 0;
}
/* https://tools.ietf.org/html/rfc4648#section-4 */
char base64char(int x) {
static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
return table[x & 63];
}
int unbase64char(char c) {
unsigned offset;
if (c >= 'A' && c <= 'Z')
return c - 'A';
offset = 'Z' - 'A' + 1;
if (c >= 'a' && c <= 'z')
return c - 'a' + offset;
offset += 'z' - 'a' + 1;
if (c >= '0' && c <= '9')
return c - '0' + offset;
offset += '9' - '0' + 1;
if (c == '+')
return offset;
offset++;
if (c == '/')
return offset;
return -EINVAL;
}
ssize_t base64mem(const void *p, size_t l, char **out) {
char *r, *z;
const uint8_t *x;
assert(p || l == 0);
assert(out);
/* three input bytes makes four output bytes, padding is added so we must round up */
z = r = malloc(4 * (l + 2) / 3 + 1);
if (!r)
return -ENOMEM;
for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
*(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
*(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */
}
switch (l % 3) {
case 2:
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
*(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */
*(z++) = '=';
break;
case 1:
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
*(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
*(z++) = '=';
*(z++) = '=';
break;
}
*z = 0;
*out = r;
return z - r;
}
static int base64_append_width(
char **prefix, int plen,
const char *sep, int indent,
const void *p, size_t l,
int width) {
_cleanup_free_ char *x = NULL;
char *t, *s;
ssize_t len, slen, avail, line, lines;
len = base64mem(p, l, &x);
if (len <= 0)
return len;
lines = DIV_ROUND_UP(len, width);
slen = strlen_ptr(sep);
if (lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1))
return -ENOMEM;
t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
if (!t)
return -ENOMEM;
memcpy_safe(t + plen, sep, slen);
for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) {
int act = MIN(width, avail);
if (line > 0 || sep) {
memset(s, ' ', indent);
s += indent;
}
memcpy(s, x + width * line, act);
s += act;
*(s++) = line < lines - 1 ? '\n' : '\0';
avail -= act;
}
assert(avail == 0);
*prefix = t;
return 0;
}
int base64_append(
char **prefix, int plen,
const void *p, size_t l,
int indent, int width) {
if (plen > width / 2 || plen + indent > width)
/* leave indent on the left, keep last column free */
return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
else
/* leave plen on the left, keep last column free */
return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
}
static int unbase64_next(const char **p, size_t *l) {
int ret;
assert(p);
assert(l);
/* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We
* greedily skip all preceding and all following whitespace. */
for (;;) {
if (*l == 0)
return -EPIPE;
if (!strchr(WHITESPACE, **p))
break;
/* Skip leading whitespace */
(*p)++, (*l)--;
}
if (**p == '=')
ret = INT_MAX; /* return padding as INT_MAX */
else {
ret = unbase64char(**p);
if (ret < 0)
return ret;
}
for (;;) {
(*p)++, (*l)--;
if (*l == 0)
break;
if (!strchr(WHITESPACE, **p))
break;
/* Skip following whitespace */
}
return ret;
}
int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
_cleanup_free_ uint8_t *buf = NULL;
const char *x;
uint8_t *z;
size_t len;
assert(p || l == 0);
assert(ret);
assert(ret_size);
if (l == (size_t) -1)
l = strlen(p);
/* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra
* bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0);
buf = malloc(len + 1);
if (!buf)
return -ENOMEM;
for (x = p, z = buf;;) {
int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
a = unbase64_next(&x, &l);
if (a == -EPIPE) /* End of string */
break;
if (a < 0)
return a;
if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
return -EINVAL;
b = unbase64_next(&x, &l);
if (b < 0)
return b;
if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
return -EINVAL;
c = unbase64_next(&x, &l);
if (c < 0)
return c;
d = unbase64_next(&x, &l);
if (d < 0)
return d;
if (c == INT_MAX) { /* Padding at the third character */
if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
return -EINVAL;
/* b == 00YY0000 */
if (b & 15)
return -EINVAL;
if (l > 0) /* Trailing rubbish? */
return -ENAMETOOLONG;
*(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
break;
}
if (d == INT_MAX) {
/* c == 00ZZZZ00 */
if (c & 3)
return -EINVAL;
if (l > 0) /* Trailing rubbish? */
return -ENAMETOOLONG;
*(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
*(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
break;
}
*(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
*(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
*(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */
}
*z = 0;
*ret_size = (size_t) (z - buf);
*ret = TAKE_PTR(buf);
return 0;
}
void hexdump(FILE *f, const void *p, size_t s) {
const uint8_t *b = p;
unsigned n = 0;
assert(b || s == 0);
if (!f)
f = stdout;
while (s > 0) {
size_t i;
fprintf(f, "%04x ", n);
for (i = 0; i < 16; i++) {
if (i >= s)
fputs(" ", f);
else
fprintf(f, "%02x ", b[i]);
if (i == 7)
fputc(' ', f);
}
fputc(' ', f);
for (i = 0; i < 16; i++) {
if (i >= s)
fputc(' ', f);
else
fputc(isprint(b[i]) ? (char) b[i] : '.', f);
}
fputc('\n', f);
if (s < 16)
break;
n += 16;
b += 16;
s -= 16;
}
}
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include "macro.h"
char octchar(int x) _const_;
int unoctchar(char c) _const_;
char decchar(int x) _const_;
int undecchar(char c) _const_;
char hexchar(int x) _const_;
int unhexchar(char c) _const_;
char *hexmem(const void *p, size_t l);
int unhexmem(const char *p, size_t l, void **mem, size_t *len);
char base32hexchar(int x) _const_;
int unbase32hexchar(char c) _const_;
char base64char(int x) _const_;
int unbase64char(char c) _const_;
char *base32hexmem(const void *p, size_t l, bool padding);
int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
ssize_t base64mem(const void *p, size_t l, char **out);
int base64_append(char **prefix, int plen,
const void *p, size_t l,
int margin, int width);
int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
void hexdump(FILE *f, const void *p, size_t s);

View File

@@ -0,0 +1,314 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hostname-util.h"
#include "macro.h"
#include "string-util.h"
#if 0 /* NM_IGNORED */
bool hostname_is_set(void) {
struct utsname u;
assert_se(uname(&u) >= 0);
if (isempty(u.nodename))
return false;
/* This is the built-in kernel default host name */
if (streq(u.nodename, "(none)"))
return false;
return true;
}
char* gethostname_malloc(void) {
struct utsname u;
/* This call tries to return something useful, either the actual hostname
* or it makes something up. The only reason it might fail is OOM.
* It might even return "localhost" if that's set. */
assert_se(uname(&u) >= 0);
if (isempty(u.nodename) || streq(u.nodename, "(none)"))
return strdup(FALLBACK_HOSTNAME);
return strdup(u.nodename);
}
#endif /* NM_IGNORED */
int gethostname_strict(char **ret) {
struct utsname u;
char *k;
/* This call will rather fail than make up a name. It will not return "localhost" either. */
assert_se(uname(&u) >= 0);
if (isempty(u.nodename))
return -ENXIO;
if (streq(u.nodename, "(none)"))
return -ENXIO;
if (is_localhost(u.nodename))
return -ENXIO;
k = strdup(u.nodename);
if (!k)
return -ENOMEM;
*ret = k;
return 0;
}
bool valid_ldh_char(char c) {
return
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '-';
}
/**
* Check if s looks like a valid host name or FQDN. This does not do
* full DNS validation, but only checks if the name is composed of
* allowed characters and the length is not above the maximum allowed
* by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
* allow_trailing_dot is true and at least two components are present
* in the name. Note that due to the restricted charset and length
* this call is substantially more conservative than
* dns_name_is_valid().
*/
bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
unsigned n_dots = 0;
const char *p;
bool dot, hyphen;
if (isempty(s))
return false;
/* Doesn't accept empty hostnames, hostnames with
* leading dots, and hostnames with multiple dots in a
* sequence. Also ensures that the length stays below
* HOST_NAME_MAX. */
for (p = s, dot = hyphen = true; *p; p++)
if (*p == '.') {
if (dot || hyphen)
return false;
dot = true;
hyphen = false;
n_dots++;
} 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
* up to 255 characters */
return false;
return true;
}
char* hostname_cleanup(char *s) {
char *p, *d;
bool dot, hyphen;
assert(s);
for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++)
if (*p == '.') {
if (dot || hyphen)
continue;
*(d++) = '.';
dot = true;
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 (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;
}
bool is_localhost(const char *hostname) {
assert(hostname);
/* This tries to identify local host and domain names
* described in RFC6761 plus the redhatism of localdomain */
return strcaseeq(hostname, "localhost") ||
strcaseeq(hostname, "localhost.") ||
strcaseeq(hostname, "localhost.localdomain") ||
strcaseeq(hostname, "localhost.localdomain.") ||
endswith_no_case(hostname, ".localhost") ||
endswith_no_case(hostname, ".localhost.") ||
endswith_no_case(hostname, ".localhost.localdomain") ||
endswith_no_case(hostname, ".localhost.localdomain.");
}
#if 0 /* NM_IGNORED */
bool is_gateway_hostname(const char *hostname) {
assert(hostname);
/* This tries to identify the valid syntaxes for the our
* synthetic "gateway" host. */
return
strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.")
#if ENABLE_COMPAT_GATEWAY_HOSTNAME
|| strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.")
#endif
;
}
int sethostname_idempotent(const char *s) {
char buf[HOST_NAME_MAX + 1] = {};
assert(s);
if (gethostname(buf, sizeof(buf)) < 0)
return -errno;
if (streq(buf, s))
return 0;
if (sethostname(s, strlen(s)) < 0)
return -errno;
return 1;
}
int shorten_overlong(const char *s, char **ret) {
char *h, *p;
/* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
* whatever comes earlier. */
assert(s);
h = strdup(s);
if (!h)
return -ENOMEM;
if (hostname_is_valid(h, false)) {
*ret = h;
return 0;
}
p = strchr(h, '.');
if (p)
*p = 0;
strshorten(h, HOST_NAME_MAX);
if (!hostname_is_valid(h, false)) {
free(h);
return -EDOM;
}
*ret = h;
return 1;
}
int read_etc_hostname_stream(FILE *f, char **ret) {
int r;
assert(f);
assert(ret);
for (;;) {
_cleanup_free_ char *line = NULL;
char *p;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
return -ENOENT;
p = strstrip(line);
/* File may have empty lines or comments, ignore them */
if (!IN_SET(*p, '\0', '#')) {
char *copy;
hostname_cleanup(p); /* normalize the hostname */
if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */
return -EBADMSG;
copy = strdup(p);
if (!copy)
return -ENOMEM;
*ret = copy;
return 0;
}
}
}
int read_etc_hostname(const char *path, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
assert(ret);
if (!path)
path = "/etc/hostname";
f = fopen(path, "re");
if (!f)
return -errno;
return read_etc_hostname_stream(f, ret);
}
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include <stdio.h>
#include "macro.h"
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);
#define machine_name_is_valid(s) hostname_is_valid(s, false)
bool is_localhost(const char *hostname);
bool is_gateway_hostname(const char *hostname);
int sethostname_idempotent(const char *s);
int shorten_overlong(const char *s, char **ret);
int read_etc_hostname_stream(FILE *f, char **ret);
int read_etc_hostname(const char *path, char **ret);

View File

@@ -0,0 +1,627 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <arpa/inet.h>
#include <endian.h>
#include <errno.h>
#include <net/if.h>
#include <stdint.h>
#include <stdlib.h>
#include "alloc-util.h"
#include "in-addr-util.h"
#include "macro.h"
#include "parse-util.h"
#include "util.h"
bool in4_addr_is_null(const struct in_addr *a) {
assert(a);
return a->s_addr == 0;
}
int in_addr_is_null(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
return in4_addr_is_null(&u->in);
if (family == AF_INET6)
return IN6_IS_ADDR_UNSPECIFIED(&u->in6);
return -EAFNOSUPPORT;
}
bool in4_addr_is_link_local(const struct in_addr *a) {
assert(a);
return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
}
int in_addr_is_link_local(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
return in4_addr_is_link_local(&u->in);
if (family == AF_INET6)
return IN6_IS_ADDR_LINKLOCAL(&u->in6);
return -EAFNOSUPPORT;
}
int in_addr_is_multicast(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
return IN_MULTICAST(be32toh(u->in.s_addr));
if (family == AF_INET6)
return IN6_IS_ADDR_MULTICAST(&u->in6);
return -EAFNOSUPPORT;
}
bool in4_addr_is_localhost(const struct in_addr *a) {
assert(a);
/* All of 127.x.x.x is localhost. */
return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
}
int in_addr_is_localhost(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
return in4_addr_is_localhost(&u->in);
if (family == AF_INET6)
return IN6_IS_ADDR_LOOPBACK(&u->in6);
return -EAFNOSUPPORT;
}
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
assert(a);
assert(b);
if (family == AF_INET)
return a->in.s_addr == b->in.s_addr;
if (family == AF_INET6)
return
a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
return -EAFNOSUPPORT;
}
int in_addr_prefix_intersect(
int family,
const union in_addr_union *a,
unsigned aprefixlen,
const union in_addr_union *b,
unsigned bprefixlen) {
unsigned m;
assert(a);
assert(b);
/* Checks whether there are any addresses that are in both
* networks */
m = MIN(aprefixlen, bprefixlen);
if (family == AF_INET) {
uint32_t x, nm;
x = be32toh(a->in.s_addr ^ b->in.s_addr);
nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
return (x & nm) == 0;
}
if (family == AF_INET6) {
unsigned i;
if (m > 128)
m = 128;
for (i = 0; i < 16; i++) {
uint8_t x, nm;
x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
if (m < 8)
nm = 0xFF << (8 - m);
else
nm = 0xFF;
if ((x & nm) != 0)
return 0;
if (m > 8)
m -= 8;
else
m = 0;
}
return 1;
}
return -EAFNOSUPPORT;
}
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
assert(u);
/* Increases the network part of an address by one. Returns
* positive it that succeeds, or 0 if this overflows. */
if (prefixlen <= 0)
return 0;
if (family == AF_INET) {
uint32_t c, n;
if (prefixlen > 32)
prefixlen = 32;
c = be32toh(u->in.s_addr);
n = c + (1UL << (32 - prefixlen));
if (n < c)
return 0;
n &= 0xFFFFFFFFUL << (32 - prefixlen);
u->in.s_addr = htobe32(n);
return 1;
}
if (family == AF_INET6) {
struct in6_addr add = {}, result;
uint8_t overflow = 0;
unsigned i;
if (prefixlen > 128)
prefixlen = 128;
/* First calculate what we have to add */
add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
for (i = 16; i > 0; i--) {
unsigned j = i - 1;
result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
}
if (overflow)
return 0;
u->in6 = result;
return 1;
}
return -EAFNOSUPPORT;
}
int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
char *x;
size_t l;
assert(u);
assert(ret);
if (family == AF_INET)
l = INET_ADDRSTRLEN;
else if (family == AF_INET6)
l = INET6_ADDRSTRLEN;
else
return -EAFNOSUPPORT;
x = new(char, l);
if (!x)
return -ENOMEM;
errno = 0;
if (!inet_ntop(family, u, x, l)) {
free(x);
return errno > 0 ? -errno : -EINVAL;
}
*ret = x;
return 0;
}
int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
size_t l;
char *x;
int r;
assert(u);
assert(ret);
/* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly
* handle IPv6 link-local addresses. */
if (family != AF_INET6)
goto fallback;
if (ifindex <= 0)
goto fallback;
r = in_addr_is_link_local(family, u);
if (r < 0)
return r;
if (r == 0)
goto fallback;
l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1;
x = new(char, l);
if (!x)
return -ENOMEM;
errno = 0;
if (!inet_ntop(family, u, x, l)) {
free(x);
return errno > 0 ? -errno : -EINVAL;
}
sprintf(strchr(x, 0), "%%%i", ifindex);
*ret = x;
return 0;
fallback:
return in_addr_to_string(family, u, ret);
}
int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
union in_addr_union buffer;
assert(s);
if (!IN_SET(family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
errno = 0;
if (inet_pton(family, s, ret ?: &buffer) <= 0)
return errno > 0 ? -errno : -EINVAL;
return 0;
}
int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret) {
int r;
assert(s);
r = in_addr_from_string(AF_INET, s, ret);
if (r >= 0) {
if (ret_family)
*ret_family = AF_INET;
return 0;
}
r = in_addr_from_string(AF_INET6, s, ret);
if (r >= 0) {
if (ret_family)
*ret_family = AF_INET6;
return 0;
}
return -EINVAL;
}
#if 0 /* NM_IGNORED */
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
_cleanup_free_ char *buf = NULL;
const char *suffix;
int r, ifi = 0;
assert(s);
assert(family);
assert(ret);
/* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
* if one is found. */
suffix = strchr(s, '%');
if (suffix) {
if (ifindex) {
/* If we shall return the interface index, try to parse it */
r = parse_ifindex(suffix + 1, &ifi);
if (r < 0) {
unsigned u;
u = if_nametoindex(suffix + 1);
if (u <= 0)
return -errno;
ifi = (int) u;
}
}
buf = strndup(s, suffix - s);
if (!buf)
return -ENOMEM;
s = buf;
}
r = in_addr_from_string_auto(s, family, ret);
if (r < 0)
return r;
if (ifindex)
*ifindex = ifi;
return r;
}
#endif /* NM_IGNORED */
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
assert(addr);
return 32U - u32ctz(be32toh(addr->s_addr));
}
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
assert(addr);
assert(prefixlen <= 32);
/* Shifting beyond 32 is not defined, handle this specially. */
if (prefixlen == 0)
addr->s_addr = 0;
else
addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
return addr;
}
int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
uint8_t msb_octet = *(uint8_t*) addr;
/* addr may not be aligned, so make sure we only access it byte-wise */
assert(addr);
assert(prefixlen);
if (msb_octet < 128)
/* class A, leading bits: 0 */
*prefixlen = 8;
else if (msb_octet < 192)
/* class B, leading bits 10 */
*prefixlen = 16;
else if (msb_octet < 224)
/* class C, leading bits 110 */
*prefixlen = 24;
else
/* class D or E, no default prefixlen */
return -ERANGE;
return 0;
}
int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
unsigned char prefixlen;
int r;
assert(addr);
assert(mask);
r = in4_addr_default_prefixlen(addr, &prefixlen);
if (r < 0)
return r;
in4_addr_prefixlen_to_netmask(mask, prefixlen);
return 0;
}
#if 0 /* NM_IGNORED */
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
assert(addr);
if (family == AF_INET) {
struct in_addr mask;
if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen))
return -EINVAL;
addr->in.s_addr &= mask.s_addr;
return 0;
}
if (family == AF_INET6) {
unsigned i;
for (i = 0; i < 16; i++) {
uint8_t mask;
if (prefixlen >= 8) {
mask = 0xFF;
prefixlen -= 8;
} else {
mask = 0xFF << (8 - prefixlen);
prefixlen = 0;
}
addr->in6.s6_addr[i] &= mask;
}
return 0;
}
return -EAFNOSUPPORT;
}
int in_addr_prefix_covers(int family,
const union in_addr_union *prefix,
unsigned char prefixlen,
const union in_addr_union *address) {
union in_addr_union masked_prefix, masked_address;
int r;
assert(prefix);
assert(address);
masked_prefix = *prefix;
r = in_addr_mask(family, &masked_prefix, prefixlen);
if (r < 0)
return r;
masked_address = *address;
r = in_addr_mask(family, &masked_address, prefixlen);
if (r < 0)
return r;
return in_addr_equal(family, &masked_prefix, &masked_address);
}
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) {
uint8_t u;
int r;
if (!IN_SET(family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
r = safe_atou8(p, &u);
if (r < 0)
return r;
if (u > FAMILY_ADDRESS_SIZE(family) * 8)
return -ERANGE;
*ret = u;
return 0;
}
int in_addr_prefix_from_string_internal(
const char *p,
bool use_default_prefixlen,
int family,
union in_addr_union *ret_prefix,
unsigned char *ret_prefixlen) {
_cleanup_free_ char *str = NULL;
union in_addr_union buffer;
const char *e, *l;
unsigned char k;
int r;
assert(p);
if (!IN_SET(family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
e = strchr(p, '/');
if (e) {
str = strndup(p, e - p);
if (!str)
return -ENOMEM;
l = str;
} else
l = p;
r = in_addr_from_string(family, l, &buffer);
if (r < 0)
return r;
if (e) {
r = in_addr_parse_prefixlen(family, e+1, &k);
if (r < 0)
return r;
} else if (use_default_prefixlen) {
if (family == AF_INET) {
r = in4_addr_default_prefixlen(&buffer.in, &k);
if (r < 0)
return r;
} else
k = 0;
} else
k = FAMILY_ADDRESS_SIZE(family) * 8;
if (ret_prefix)
*ret_prefix = buffer;
if (ret_prefixlen)
*ret_prefixlen = k;
return 0;
}
int in_addr_prefix_from_string_auto_internal(
const char *p,
bool use_default_prefixlen,
int *ret_family,
union in_addr_union *ret_prefix,
unsigned char *ret_prefixlen) {
_cleanup_free_ char *str = NULL;
union in_addr_union buffer;
const char *e, *l;
unsigned char k;
int family, r;
assert(p);
e = strchr(p, '/');
if (e) {
str = strndup(p, e - p);
if (!str)
return -ENOMEM;
l = str;
} else
l = p;
r = in_addr_from_string_auto(l, &family, &buffer);
if (r < 0)
return r;
if (e) {
r = in_addr_parse_prefixlen(family, e+1, &k);
if (r < 0)
return r;
} else if (use_default_prefixlen) {
if (family == AF_INET) {
r = in4_addr_default_prefixlen(&buffer.in, &k);
if (r < 0)
return r;
} else
k = 0;
} else
k = FAMILY_ADDRESS_SIZE(family) * 8;
if (ret_family)
*ret_family = family;
if (ret_prefix)
*ret_prefix = buffer;
if (ret_prefixlen)
*ret_prefixlen = k;
return 0;
}
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);
}
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);
if (r != 0)
return r;
return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
}
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 */

View File

@@ -0,0 +1,72 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <netinet/in.h>
#include <stddef.h>
#include <sys/socket.h>
#include "hash-funcs.h"
#include "macro.h"
#include "util.h"
union in_addr_union {
struct in_addr in;
struct in6_addr in6;
};
struct in_addr_data {
int family;
union in_addr_union address;
};
bool in4_addr_is_null(const struct in_addr *a);
int in_addr_is_null(int family, const union in_addr_union *u);
int in_addr_is_multicast(int family, const union in_addr_union *u);
bool in4_addr_is_link_local(const struct in_addr *a);
int in_addr_is_link_local(int family, const union in_addr_union *u);
bool in4_addr_is_localhost(const struct in_addr *a);
int in_addr_is_localhost(int family, const union in_addr_union *u);
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret);
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr);
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
int in_addr_prefix_from_string_internal(const char *p, bool use_default_prefixlen, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
int in_addr_prefix_from_string_auto_internal(const char *p, bool use_default_prefixlen, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
static inline int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
return in_addr_prefix_from_string_internal(p, false, family, ret_prefix, ret_prefixlen);
}
static inline int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
return in_addr_prefix_from_string_auto_internal(p, false, ret_family, ret_prefix, ret_prefixlen);
}
static inline int in_addr_default_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
return in_addr_prefix_from_string_internal(p, true, family, ret_prefix, ret_prefixlen);
}
static inline int in_addr_default_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
return in_addr_prefix_from_string_auto_internal(p, true, ret_family, ret_prefix, ret_prefixlen);
}
static inline size_t FAMILY_ADDRESS_SIZE(int family) {
assert(IN_SET(family, AF_INET, AF_INET6));
return family == AF_INET6 ? 16 : 4;
}
/* Workaround for clang, explicitly specify the maximum-size element here.
* See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
extern const struct hash_ops in_addr_data_hash_ops;

View File

@@ -0,0 +1,256 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include "io-util.h"
#include "time-util.h"
int flush_fd(int fd) {
struct pollfd pollfd = {
.fd = fd,
.events = POLLIN,
};
int count = 0;
/* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
* read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read
* (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used
* was set to non-blocking too. */
for (;;) {
char buf[LINE_MAX];
ssize_t l;
int r;
r = poll(&pollfd, 1, 0);
if (r < 0) {
if (errno == EINTR)
continue;
return -errno;
} else if (r == 0)
return count;
l = read(fd, buf, sizeof(buf));
if (l < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN)
return count;
return -errno;
} else if (l == 0)
return count;
count += (int) l;
}
}
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
uint8_t *p = buf;
ssize_t n = 0;
assert(fd >= 0);
assert(buf);
/* If called with nbytes == 0, let's call read() at least
* once, to validate the operation */
if (nbytes > (size_t) SSIZE_MAX)
return -EINVAL;
do {
ssize_t k;
k = read(fd, p, nbytes);
if (k < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN && do_poll) {
/* We knowingly ignore any return value here,
* and expect that any error/EOF is reported
* via read() */
(void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
continue;
}
return n > 0 ? n : -errno;
}
if (k == 0)
return n;
assert((size_t) k <= nbytes);
p += k;
nbytes -= k;
n += k;
} while (nbytes > 0);
return n;
}
int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
ssize_t n;
n = loop_read(fd, buf, nbytes, do_poll);
if (n < 0)
return (int) n;
if ((size_t) n != nbytes)
return -EIO;
return 0;
}
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
const uint8_t *p = buf;
assert(fd >= 0);
assert(buf);
if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
return -EINVAL;
do {
ssize_t k;
k = write(fd, p, nbytes);
if (k < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN && do_poll) {
/* We knowingly ignore any return value here,
* and expect that any error/EOF is reported
* via write() */
(void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
continue;
}
return -errno;
}
if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
return -EIO;
assert((size_t) k <= nbytes);
p += k;
nbytes -= k;
} while (nbytes > 0);
return 0;
}
int pipe_eof(int fd) {
struct pollfd pollfd = {
.fd = fd,
.events = POLLIN|POLLHUP,
};
int r;
r = poll(&pollfd, 1, 0);
if (r < 0)
return -errno;
if (r == 0)
return 0;
return pollfd.revents & POLLHUP;
}
int fd_wait_for_event(int fd, int event, usec_t t) {
struct pollfd pollfd = {
.fd = fd,
.events = event,
};
struct timespec ts;
int r;
r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
if (r < 0)
return -errno;
if (r == 0)
return 0;
return pollfd.revents;
}
static size_t nul_length(const uint8_t *p, size_t sz) {
size_t n = 0;
while (sz > 0) {
if (*p != 0)
break;
n++;
p++;
sz--;
}
return n;
}
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
const uint8_t *q, *w, *e;
ssize_t l;
q = w = p;
e = q + sz;
while (q < e) {
size_t n;
n = nul_length(q, e - q);
/* If there are more than the specified run length of
* NUL bytes, or if this is the beginning or the end
* of the buffer, then seek instead of write */
if ((n > run_length) ||
(n > 0 && q == p) ||
(n > 0 && q + n >= e)) {
if (q > w) {
l = write(fd, w, q - w);
if (l < 0)
return -errno;
if (l != q -w)
return -EIO;
}
if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
return -errno;
q += n;
w = q;
} else if (n > 0)
q += n;
else
q++;
}
if (q > w) {
l = write(fd, w, q - w);
if (l < 0)
return -errno;
if (l != q - w)
return -EIO;
}
return q - (const uint8_t*) p;
}

View File

@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "macro.h"
#include "time-util.h"
int flush_fd(int fd);
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
int pipe_eof(int fd);
int fd_wait_for_event(int fd, int event, usec_t timeout);
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) {
size_t j, r = 0;
for (j = 0; j < n; j++)
r += i[j].iov_len;
return r;
}
static inline size_t IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) {
size_t j;
for (j = 0; j < n; j++) {
size_t sub;
if (_unlikely_(k <= 0))
break;
sub = MIN(i[j].iov_len, k);
i[j].iov_len -= sub;
i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
k -= sub;
}
return k;
}
static inline bool FILE_SIZE_VALID(uint64_t l) {
/* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than
* 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */
return (l >> 63) == 0;
}
static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
/* Same as above, but allows one extra value: -1 as indication for infinity. */
if (l == (uint64_t) -1)
return true;
return FILE_SIZE_VALID(l);
}
#define IOVEC_INIT(base, len) { .iov_base = (base), .iov_len = (len) }
#define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len)
#define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string))
#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string)

View File

@@ -0,0 +1,171 @@
/* 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) \
t *name
/* The pointers in the linked list's items. Use this in the item structure */
#define LIST_FIELDS(t,name) \
t *name##_next, *name##_prev
/* Initialize the list's head */
#define LIST_HEAD_INIT(head) \
do { \
(head) = NULL; \
} while (false)
/* Initialize a list item */
#define LIST_INIT(name,item) \
do { \
typeof(*(item)) *_item = (item); \
assert(_item); \
_item->name##_prev = _item->name##_next = NULL; \
} while (false)
/* Prepend an item to the list */
#define LIST_PREPEND(name,head,item) \
do { \
typeof(*(head)) **_head = &(head), *_item = (item); \
assert(_item); \
if ((_item->name##_next = *_head)) \
_item->name##_next->name##_prev = _item; \
_item->name##_prev = NULL; \
*_head = _item; \
} while (false)
/* Append an item to the list */
#define LIST_APPEND(name,head,item) \
do { \
typeof(*(head)) **_hhead = &(head), *_tail; \
LIST_FIND_TAIL(name, *_hhead, _tail); \
LIST_INSERT_AFTER(name, *_hhead, _tail, item); \
} while (false)
/* Remove an item from the list */
#define LIST_REMOVE(name,head,item) \
do { \
typeof(*(head)) **_head = &(head), *_item = (item); \
assert(_item); \
if (_item->name##_next) \
_item->name##_next->name##_prev = _item->name##_prev; \
if (_item->name##_prev) \
_item->name##_prev->name##_next = _item->name##_next; \
else { \
assert(*_head == _item); \
*_head = _item->name##_next; \
} \
_item->name##_next = _item->name##_prev = NULL; \
} while (false)
/* Find the head of the list */
#define LIST_FIND_HEAD(name,item,head) \
do { \
typeof(*(item)) *_item = (item); \
if (!_item) \
(head) = NULL; \
else { \
while (_item->name##_prev) \
_item = _item->name##_prev; \
(head) = _item; \
} \
} while (false)
/* Find the tail of the list */
#define LIST_FIND_TAIL(name,item,tail) \
do { \
typeof(*(item)) *_item = (item); \
if (!_item) \
(tail) = NULL; \
else { \
while (_item->name##_next) \
_item = _item->name##_next; \
(tail) = _item; \
} \
} while (false)
/* Insert an item after another one (a = where, b = what) */
#define LIST_INSERT_AFTER(name,head,a,b) \
do { \
typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
assert(_b); \
if (!_a) { \
if ((_b->name##_next = *_head)) \
_b->name##_next->name##_prev = _b; \
_b->name##_prev = NULL; \
*_head = _b; \
} else { \
if ((_b->name##_next = _a->name##_next)) \
_b->name##_next->name##_prev = _b; \
_b->name##_prev = _a; \
_a->name##_next = _b; \
} \
} while (false)
/* Insert an item before another one (a = where, b = what) */
#define LIST_INSERT_BEFORE(name,head,a,b) \
do { \
typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
assert(_b); \
if (!_a) { \
if (!*_head) { \
_b->name##_next = NULL; \
_b->name##_prev = NULL; \
*_head = _b; \
} else { \
typeof(*(head)) *_tail = (head); \
while (_tail->name##_next) \
_tail = _tail->name##_next; \
_b->name##_next = NULL; \
_b->name##_prev = _tail; \
_tail->name##_next = _b; \
} \
} else { \
if ((_b->name##_prev = _a->name##_prev)) \
_b->name##_prev->name##_next = _b; \
else \
*_head = _b; \
_b->name##_next = _a; \
_a->name##_prev = _b; \
} \
} while (false)
#define LIST_JUST_US(name,item) \
(!(item)->name##_prev && !(item)->name##_next) \
#define LIST_FOREACH(name,i,head) \
for ((i) = (head); (i); (i) = (i)->name##_next)
#define LIST_FOREACH_SAFE(name,i,n,head) \
for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
#define LIST_FOREACH_BEFORE(name,i,p) \
for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
#define LIST_FOREACH_AFTER(name,i,p) \
for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
/* Iterate through all the members of the list p is included in, but skip over p */
#define LIST_FOREACH_OTHERS(name,i,p) \
for (({ \
(i) = (p); \
while ((i) && (i)->name##_prev) \
(i) = (i)->name##_prev; \
if ((i) == (p)) \
(i) = (p)->name##_next; \
}); \
(i); \
(i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next)
/* Loop starting from p->next until p->prev.
p can be adjusted meanwhile. */
#define LIST_LOOP_BUT_ONE(name,i,head,p) \
for ((i) = (p)->name##_next ? (p)->name##_next : (head); \
(i) != (p); \
(i) = (i)->name##_next ? (i)->name##_next : (head))
#define LIST_IS_EMPTY(head) \
(!(head))

View File

@@ -0,0 +1,331 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <syslog.h>
#include "macro.h"
/* Some structures we reference but don't want to pull in headers for */
struct iovec;
struct signalfd_siginfo;
typedef enum LogRealm {
LOG_REALM_SYSTEMD,
LOG_REALM_UDEV,
_LOG_REALM_MAX,
} LogRealm;
#ifndef LOG_REALM
# define LOG_REALM LOG_REALM_SYSTEMD
#endif
typedef enum LogTarget{
LOG_TARGET_CONSOLE,
LOG_TARGET_CONSOLE_PREFIXED,
LOG_TARGET_KMSG,
LOG_TARGET_JOURNAL,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
LOG_TARGET_NULL,
_LOG_TARGET_MAX,
_LOG_TARGET_INVALID = -1
} LogTarget;
/* Note to readers: << and >> have lower precedence than & and | */
#define LOG_REALM_PLUS_LEVEL(realm, level) ((realm) << 10 | (level))
#define LOG_REALM_REMOVE_LEVEL(realm_level) ((realm_level) >> 10)
#define SYNTHETIC_ERRNO(num) (1 << 30 | (num))
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define ERRNO_VALUE(val) (abs(val) & 255)
void log_set_target(LogTarget target);
void log_set_max_level_realm(LogRealm realm, int level);
#define log_set_max_level(level) \
log_set_max_level_realm(LOG_REALM, (level))
void log_set_facility(int facility);
int log_set_target_from_string(const char *e);
int log_set_max_level_from_string_realm(LogRealm realm, const char *e);
#define log_set_max_level_from_string(e) \
log_set_max_level_from_string_realm(LOG_REALM, (e))
void log_show_color(bool b);
bool log_get_show_color(void) _pure_;
void log_show_location(bool b);
bool log_get_show_location(void) _pure_;
int log_show_color_from_string(const char *e);
int log_show_location_from_string(const char *e);
LogTarget log_get_target(void) _pure_;
#if 0 /* NM_IGNORED */
int log_get_max_level_realm(LogRealm realm) _pure_;
#endif /* NM_IGNORED */
#define log_get_max_level() \
log_get_max_level_realm(LOG_REALM)
/* Functions below that open and close logs or configure logging based on the
* environment should not be called from library code — this is always a job
* for the application itself.
*/
int log_open(void);
void log_close(void);
void log_forget_fds(void);
void log_parse_environment_realm(LogRealm realm);
#define log_parse_environment() \
log_parse_environment_realm(LOG_REALM)
#if 0 /* NM_IGNORED */
int log_dispatch_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *extra,
const char *extra_field,
char *buffer);
int log_internal_realm(
int level,
int error,
const char *file,
int line,
const char *func,
const char *format, ...) _printf_(6,7);
#endif /* NM_IGNORED */
#define log_internal(level, ...) \
log_internal_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__)
#if 0 /* NM_IGNORED */
int log_internalv_realm(
int level,
int error,
const char *file,
int line,
const char *func,
const char *format,
va_list ap) _printf_(6,0);
#define log_internalv(level, ...) \
log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__)
/* Realm is fixed to LOG_REALM_SYSTEMD for those */
int log_object_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *extra_field,
const char *extra,
const char *format, ...) _printf_(10,11);
int log_struct_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const char *format, ...) _printf_(6,0) _sentinel_;
int log_oom_internal(
LogRealm realm,
const char *file,
int line,
const char *func);
int log_format_iovec(
struct iovec *iovec,
size_t iovec_len,
size_t *n,
bool newline_separator,
int error,
const char *format,
va_list ap) _printf_(6, 0);
int log_struct_iovec_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const struct iovec *input_iovec,
size_t n_input_iovec);
/* This modifies the buffer passed! */
int log_dump_internal(
int level,
int error,
const char *file,
int line,
const char *func,
char *buffer);
/* Logging for various assertions */
_noreturn_ void log_assert_failed_realm(
LogRealm realm,
const char *text,
const char *file,
int line,
const char *func);
#define log_assert_failed(text, ...) \
log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__)
_noreturn_ void log_assert_failed_unreachable_realm(
LogRealm realm,
const char *text,
const char *file,
int line,
const char *func);
#define log_assert_failed_unreachable(text, ...) \
log_assert_failed_unreachable_realm(LOG_REALM, (text), __VA_ARGS__)
void log_assert_failed_return_realm(
LogRealm realm,
const char *text,
const char *file,
int line,
const char *func);
#define log_assert_failed_return(text, ...) \
log_assert_failed_return_realm(LOG_REALM, (text), __VA_ARGS__)
#define log_dispatch(level, error, buffer) \
log_dispatch_internal(level, error, __FILE__, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer)
#endif /* NM_IGNORED */
/* Logging with level */
#define log_full_errno_realm(realm, level, error, ...) \
({ \
int _level = (level), _e = (error), _realm = (realm); \
(log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \
? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \
__FILE__, __LINE__, __func__, __VA_ARGS__) \
: -ERRNO_VALUE(_e); \
})
#define log_full_errno(level, error, ...) \
log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__)
#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__)
int log_emergency_level(void);
/* Normal logging */
#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__)
/* Logging triggered by an errno-like error */
#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__)
#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__)
#ifdef LOG_TRACE
# define log_trace(...) log_debug(__VA_ARGS__)
#else
# define log_trace(...) do {} while (0)
#endif
/* Structured logging */
#define log_struct_errno(level, error, ...) \
log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \
error, __FILE__, __LINE__, __func__, __VA_ARGS__, NULL)
#define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__)
#define log_struct_iovec_errno(level, error, iovec, n_iovec) \
log_struct_iovec_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \
error, __FILE__, __LINE__, __func__, iovec, n_iovec)
#define log_struct_iovec(level, iovec, n_iovec) log_struct_iovec_errno(level, 0, iovec, n_iovec)
/* This modifies the buffer passed! */
#define log_dump(level, buffer) \
log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \
0, __FILE__, __LINE__, __func__, buffer)
#define log_oom() log_oom_internal(LOG_REALM, __FILE__, __LINE__, __func__)
bool log_on_console(void) _pure_;
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
/* Helper to prepare various field for structured logging */
#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
void log_received_signal(int level, const struct signalfd_siginfo *si);
/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */
void log_set_upgrade_syslog_to_journal(bool b);
/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */
void log_set_always_reopen_console(bool b);
/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not
* desired as we want to reuse our logging streams. It is useful however */
void log_set_open_when_needed(bool b);
/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to
* stderr, the console or kmsg */
void log_set_prohibit_ipc(bool b);
int log_dup_console(void);
int log_syntax_internal(
const char *unit,
int level,
const char *config_file,
unsigned config_line,
int error,
const char *file,
int line,
const char *func,
const char *format, ...) _printf_(9, 10);
int log_syntax_invalid_utf8_internal(
const char *unit,
int level,
const char *config_file,
unsigned config_line,
const char *file,
int line,
const char *func,
const char *rvalue);
#define log_syntax(unit, level, config_file, config_line, error, ...) \
({ \
int _level = (level), _e = (error); \
(log_get_max_level() >= LOG_PRI(_level)) \
? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
({ \
int _level = (level); \
(log_get_max_level() >= LOG_PRI(_level)) \
? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \
: -EINVAL; \
})
#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
void log_setup_service(void);

View File

@@ -0,0 +1,558 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#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)))
#ifdef __clang__
# define _alloc_(...)
#else
# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
#endif
#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 _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__))
#else
#define _fallthrough_
#endif
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
* compiler versions */
#ifndef _noreturn_
#if __STDC_VERSION__ >= 201112L
#define _noreturn_ _Noreturn
#else
#define _noreturn_ __attribute__((__noreturn__))
#endif
#endif
#if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# if defined(__has_feature)
# if __has_feature(memory_sanitizer)
# define HAS_FEATURE_MEMORY_SANITIZER 1
# endif
# endif
# if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# define HAS_FEATURE_MEMORY_SANITIZER 0
# 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 \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
#define DISABLE_WARNING_MISSING_PROTOTYPES \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
#define DISABLE_WARNING_NONNULL \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wnonnull\"")
#define DISABLE_WARNING_SHADOW \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wshadow\"")
#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
#define REENABLE_WARNING \
_Pragma("GCC diagnostic pop")
#else
#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT
#define DISABLE_WARNING_FORMAT_NONLITERAL
#define DISABLE_WARNING_MISSING_PROTOTYPES
#define DISABLE_WARNING_NONNULL
#define DISABLE_WARNING_SHADOW
#define REENABLE_WARNING
#endif
/* automake test harness */
#define EXIT_TEST_SKIP 77
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
#define XCONCATENATE(x, y) x ## y
#define CONCATENATE(x, y) XCONCATENATE(x, y)
#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
#define UNIQ __COUNTER__
/* builtins */
#if __SIZEOF_INT__ == 4
#define BUILTIN_FFS_U32(x) __builtin_ffs(x);
#elif __SIZEOF_LONG__ == 4
#define BUILTIN_FFS_U32(x) __builtin_ffsl(x);
#else
#error "neither int nor long are four bytes long?!?"
#endif
/* Rounds up */
#define ALIGN4(l) (((l) + 3) & ~3)
#define ALIGN8(l) (((l) + 7) & ~7)
#if __SIZEOF_POINTER__ == 8
#define ALIGN(l) ALIGN8(l)
#elif __SIZEOF_POINTER__ == 4
#define ALIGN(l) ALIGN4(l)
#else
#error "Wut? Pointers are neither 4 nor 8 bytes long?"
#endif
#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p)))
#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p)))
#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
static inline size_t ALIGN_TO(size_t l, size_t ali) {
return ((l + ali - 1) & ~(ali - 1));
}
#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali)))
/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
static inline unsigned long ALIGN_POWER2(unsigned long u) {
/* clz(0) is undefined */
if (u == 1)
return 1;
/* left-shift overflow is undefined */
if (__builtin_clzl(u - 1UL) < 1)
return 0;
return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
}
#ifndef __COVERITY__
# define VOID_0 ((void)0)
#else
# define VOID_0 ((void*)0)
#endif
#define ELEMENTSOF(x) \
(__builtin_choose_expr( \
!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
sizeof(x)/sizeof((x)[0]), \
VOID_0))
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
* Contrary to strlen(), this is a constant expression.
* @x: a string literal.
*/
#define STRLEN(x) (sizeof(""x"") - 1)
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*/
#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member)
#define __container_of(uniq, ptr, type, member) \
({ \
const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \
(type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \
})
#undef MAX
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
#define __MAX(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
/* evaluates to (void) if _A or _B are not constant or of different types */
#define CONST_MAX(_A, _B) \
(__builtin_choose_expr( \
__builtin_constant_p(_A) && \
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
((_A) > (_B)) ? (_A) : (_B), \
VOID_0))
/* takes two types and returns the size of the larger one */
#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
#define MAX3(x, y, z) \
({ \
const typeof(x) _c = MAX(x, y); \
MAX(_c, z); \
})
#undef MIN
#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b))
#define __MIN(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
#define MIN3(x, y, z) \
({ \
const typeof(x) _c = MIN(x, y); \
MIN(_c, z); \
})
#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
#define __LESS_BY(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \
})
#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b))
#define __CMP(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \
})
#undef CLAMP
#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high))
#define __CLAMP(xq, x, lowq, low, highq, high) \
({ \
const typeof(x) UNIQ_T(X, xq) = (x); \
const typeof(low) UNIQ_T(LOW, lowq) = (low); \
const typeof(high) UNIQ_T(HIGH, highq) = (high); \
UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \
UNIQ_T(HIGH, highq) : \
UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \
UNIQ_T(LOW, lowq) : \
UNIQ_T(X, xq); \
})
/* [(x + y - 1) / y] suffers from an integer overflow, even though the
* 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) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y))
#define __DIV_ROUND_UP(xq, x, yq, 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__
/* Use special definitions of assertion macros in order to prevent
* false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer
* for uses of assert_se() and assert_return().
*
* These definitions make expression go through a (trivial) function
* call to ensure they are not discarded. Also use ! or !! to ensure
* the boolean expressions are seen as such.
*
* This technique has been described and recommended in:
* https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects
*/
extern void __coverity_panic__(void);
static inline int __coverity_check__(int condition) {
return condition;
}
#define assert_message_se(expr, message) \
do { \
if (__coverity_check__(!(expr))) \
__coverity_panic__(); \
} while (false)
#define assert_log(expr, message) __coverity_check__(!!(expr))
#else /* ! __COVERITY__ */
#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (false)
#define assert_log(expr, message) ((_likely_(expr)) \
? (true) \
: (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
#endif /* __COVERITY__ */
#define assert_se(expr) assert_message_se(expr, #expr)
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
#define assert(expr) do {} while (false)
#else
#define assert(expr) assert_message_se(expr, #expr)
#endif
#define assert_not_reached(t) \
do { \
log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (false)
#if defined(static_assert)
#define assert_cc(expr) \
static_assert(expr, #expr);
#else
#define assert_cc(expr) \
struct CONCATENATE(_assert_struct_, __COUNTER__) { \
char x[(expr) ? 0 : -1]; \
};
#endif
#define assert_return(expr, r) \
do { \
if (!assert_log(expr, #expr)) \
return (r); \
} while (false)
#define assert_return_errno(expr, r, err) \
do { \
if (!assert_log(expr, #expr)) { \
errno = err; \
return (r); \
} \
} while (false)
#define return_with_errno(r, err) \
do { \
errno = abs(err); \
return r; \
} while (false)
#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_UINT(p) ((unsigned) ((uintptr_t) (p)))
#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_LONG(p) ((long) ((intptr_t) (p)))
#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p)))
#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p)))
#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p)))
#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p)))
#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define CHAR_TO_STR(x) ((char[2]) { x, 0 })
#define char_array_0(x) x[sizeof(x)-1] = 0;
/* Returns the number of chars needed to format variables of the
* specified type as a decimal string. Adds in extra space for a
* negative '-' prefix (hence works correctly on signed
* types). Includes space for the trailing NUL. */
#define DECIMAL_STR_MAX(type) \
(2+(sizeof(type) <= 1 ? 3 : \
sizeof(type) <= 2 ? 5 : \
sizeof(type) <= 4 ? 10 : \
sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
#define DECIMAL_STR_WIDTH(x) \
({ \
typeof(x) _x_ = (x); \
unsigned ans = 1; \
while ((_x_ /= 10) != 0) \
ans++; \
ans; \
})
#define SET_FLAG(v, flag, b) \
(v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
#define FLAGS_SET(v, flags) \
((~(v) & (flags)) == 0)
#define CASE_F(X) case X:
#define CASE_F_1(CASE, X) CASE_F(X)
#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__)
#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__)
#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__)
#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__)
#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__)
#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__)
#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__)
#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__)
#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__)
#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__)
#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__)
#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__)
#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__)
#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__)
#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__)
#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__)
#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__)
#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__)
#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__)
#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
#define FOR_EACH_MAKE_CASE(...) \
GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \
CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \
(CASE_F,__VA_ARGS__)
#define IN_SET(x, ...) \
({ \
bool _found = false; \
/* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \
* type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \
* the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \
* doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \
assert_cc((sizeof((long double[]){__VA_ARGS__})/sizeof(long double)) <= 20); \
switch(x) { \
FOR_EACH_MAKE_CASE(__VA_ARGS__) \
_found = true; \
break; \
default: \
break; \
} \
_found; \
})
#define SWAP_TWO(x, y) do { \
typeof(x) _t = (x); \
(x) = (y); \
(y) = (_t); \
} while (false)
/* Define C11 thread_local attribute even on older gcc compiler
* version */
#ifndef thread_local
/*
* Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
* see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
*/
#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
#define thread_local _Thread_local
#else
#define thread_local __thread
#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) \
func(*p); \
}
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
scope type *name##_ref(type *p) { \
if (!p) \
return NULL; \
\
assert(p->n_ref > 0); \
p->n_ref++; \
return p; \
}
#define _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, scope) \
scope type *name##_unref(type *p) { \
if (!p) \
return NULL; \
\
assert(p->n_ref > 0); \
p->n_ref--; \
if (p->n_ref > 0) \
return NULL; \
\
return free_func(p); \
}
#define DEFINE_TRIVIAL_REF_FUNC(type, name) \
_DEFINE_TRIVIAL_REF_FUNC(type, name,)
#define DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name) \
_DEFINE_TRIVIAL_REF_FUNC(type, name, static)
#define DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name) \
_DEFINE_TRIVIAL_REF_FUNC(type, name, _public_)
#define DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
_DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func,)
#define DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
_DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, static)
#define DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func) \
_DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, _public_)
#define DEFINE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
DEFINE_TRIVIAL_REF_FUNC(type, name); \
DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func);
#define DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name); \
DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func);
#define DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \
DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
#include "log.h"

View File

@@ -0,0 +1,101 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <stdint.h>
#include <stdlib.h>
#include "env-util.h"
#include "macro.h"
#include "mempool.h"
#include "process-util.h"
#include "util.h"
struct pool {
struct pool *next;
size_t n_tiles;
size_t n_used;
};
void* mempool_alloc_tile(struct mempool *mp) {
size_t i;
/* When a tile is released we add it to the list and simply
* place the next pointer at its offset 0. */
assert(mp->tile_size >= sizeof(void*));
assert(mp->at_least > 0);
if (mp->freelist) {
void *r;
r = mp->freelist;
mp->freelist = * (void**) mp->freelist;
return r;
}
if (_unlikely_(!mp->first_pool) ||
_unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) {
size_t size, n;
struct pool *p;
n = mp->first_pool ? mp->first_pool->n_tiles : 0;
n = MAX(mp->at_least, n * 2);
size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size);
n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size;
p = malloc(size);
if (!p)
return NULL;
p->next = mp->first_pool;
p->n_tiles = n;
p->n_used = 0;
mp->first_pool = p;
}
i = mp->first_pool->n_used++;
return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size;
}
void* mempool_alloc0_tile(struct mempool *mp) {
void *p;
p = mempool_alloc_tile(mp);
if (p)
memzero(p, mp->tile_size);
return p;
}
void mempool_free_tile(struct mempool *mp, void *p) {
* (void**) p = mp->freelist;
mp->freelist = p;
}
bool mempool_enabled(void) {
static int b = -1;
if (!is_main_thread())
return false;
if (!mempool_use_allowed)
b = false;
if (b < 0)
b = getenv_bool("SYSTEMD_MEMPOOL") != 0;
return b;
}
#if VALGRIND
void mempool_drop(struct mempool *mp) {
struct pool *p = mp->first_pool;
while (p) {
struct pool *n;
n = p->next;
free(p);
p = n;
}
}
#endif

View File

@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include <stddef.h>
struct pool;
struct mempool {
struct pool *first_pool;
void *freelist;
size_t tile_size;
unsigned at_least;
};
void* mempool_alloc_tile(struct mempool *mp);
void* mempool_alloc0_tile(struct mempool *mp);
void mempool_free_tile(struct mempool *mp, void *p);
#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
static struct mempool pool_name = { \
.tile_size = sizeof(tile_type), \
.at_least = alloc_at_least, \
}
extern const bool mempool_use_allowed;
bool mempool_enabled(void);
#if VALGRIND
void mempool_drop(struct mempool *mp);
#endif

View File

@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <uchar.h>
#if !HAVE_CHAR32_T
#define char32_t uint32_t
#endif
#if !HAVE_CHAR16_T
#define char16_t uint16_t
#endif

View File

@@ -0,0 +1,785 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <inttypes.h>
#include <linux/oom.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include "alloc-util.h"
#include "errno-list.h"
#include "extract-word.h"
#include "locale-util.h"
#include "macro.h"
#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) {
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;
else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
return 0;
return -EINVAL;
}
#if 0 /* NM_IGNORED */
int parse_pid(const char *s, pid_t* ret_pid) {
unsigned long ul = 0;
pid_t pid;
int r;
assert(s);
assert(ret_pid);
r = safe_atolu(s, &ul);
if (r < 0)
return r;
pid = (pid_t) ul;
if ((unsigned long) pid != ul)
return -ERANGE;
if (!pid_is_valid(pid))
return -ERANGE;
*ret_pid = pid;
return 0;
}
int parse_mode(const char *s, mode_t *ret) {
char *x;
long l;
assert(s);
assert(ret);
s += strspn(s, WHITESPACE);
if (s[0] == '-')
return -ERANGE;
errno = 0;
l = strtol(s, &x, 8);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
if (l < 0 || l > 07777)
return -ERANGE;
*ret = (mode_t) l;
return 0;
}
int parse_ifindex(const char *s, int *ret) {
int ifi, r;
r = safe_atoi(s, &ifi);
if (r < 0)
return r;
if (ifi <= 0)
return -EINVAL;
*ret = ifi;
return 0;
}
int parse_mtu(int family, const char *s, uint32_t *ret) {
uint64_t u;
size_t m;
int r;
r = parse_size(s, 1024, &u);
if (r < 0)
return r;
if (u > UINT32_MAX)
return -ERANGE;
if (family == AF_INET6)
m = IPV6_MIN_MTU; /* This is 1280 */
else
m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */
if (u < m)
return -ERANGE;
*ret = (uint32_t) u;
return 0;
}
int parse_size(const char *t, uint64_t base, uint64_t *size) {
/* Soo, sometimes we want to parse IEC binary suffixes, and
* sometimes SI decimal suffixes. This function can parse
* both. Which one is the right way depends on the
* context. Wikipedia suggests that SI is customary for
* hardware metrics and network speeds, while IEC is
* customary for most data sizes used by software and volatile
* (RAM) memory. Hence be careful which one you pick!
*
* In either case we use just K, M, G as suffix, and not Ki,
* Mi, Gi or so (as IEC would suggest). That's because that's
* frickin' ugly. But this means you really need to make sure
* to document which base you are parsing when you use this
* call. */
struct table {
const char *suffix;
unsigned long long factor;
};
static const struct table iec[] = {
{ "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
{ "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
{ "T", 1024ULL*1024ULL*1024ULL*1024ULL },
{ "G", 1024ULL*1024ULL*1024ULL },
{ "M", 1024ULL*1024ULL },
{ "K", 1024ULL },
{ "B", 1ULL },
{ "", 1ULL },
};
static const struct table si[] = {
{ "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
{ "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
{ "T", 1000ULL*1000ULL*1000ULL*1000ULL },
{ "G", 1000ULL*1000ULL*1000ULL },
{ "M", 1000ULL*1000ULL },
{ "K", 1000ULL },
{ "B", 1ULL },
{ "", 1ULL },
};
const struct table *table;
const char *p;
unsigned long long r = 0;
unsigned n_entries, start_pos = 0;
assert(t);
assert(IN_SET(base, 1000, 1024));
assert(size);
if (base == 1000) {
table = si;
n_entries = ELEMENTSOF(si);
} else {
table = iec;
n_entries = ELEMENTSOF(iec);
}
p = t;
do {
unsigned long long l, tmp;
double frac = 0;
char *e;
unsigned i;
p += strspn(p, WHITESPACE);
errno = 0;
l = strtoull(p, &e, 10);
if (errno > 0)
return -errno;
if (e == p)
return -EINVAL;
if (*p == '-')
return -ERANGE;
if (*e == '.') {
e++;
/* strtoull() itself would accept space/+/- */
if (*e >= '0' && *e <= '9') {
unsigned long long l2;
char *e2;
l2 = strtoull(e, &e2, 10);
if (errno > 0)
return -errno;
/* Ignore failure. E.g. 10.M is valid */
frac = l2;
for (; e < e2; e++)
frac /= 10;
}
}
e += strspn(e, WHITESPACE);
for (i = start_pos; i < n_entries; i++)
if (startswith(e, table[i].suffix))
break;
if (i >= n_entries)
return -EINVAL;
if (l + (frac > 0) > ULLONG_MAX / table[i].factor)
return -ERANGE;
tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
if (tmp > ULLONG_MAX - r)
return -ERANGE;
r += tmp;
if ((unsigned long long) (uint64_t) r != r)
return -ERANGE;
p = e + strlen(table[i].suffix);
start_pos = i + 1;
} while (*p);
*size = r;
return 0;
}
int parse_range(const char *t, unsigned *lower, unsigned *upper) {
_cleanup_free_ char *word = NULL;
unsigned l, u;
int r;
assert(lower);
assert(upper);
/* Extract the lower bound. */
r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
r = safe_atou(word, &l);
if (r < 0)
return r;
/* Check for the upper bound and extract it if needed */
if (!t)
/* Single number with no dashes. */
u = l;
else if (!*t)
/* Trailing dash is an error. */
return -EINVAL;
else {
r = safe_atou(t, &u);
if (r < 0)
return r;
}
*lower = l;
*upper = u;
return 0;
}
int parse_errno(const char *t) {
int r, e;
assert(t);
r = errno_from_name(t);
if (r > 0)
return r;
r = safe_atoi(t, &e);
if (r < 0)
return r;
/* 0 is also allowed here */
if (!errno_is_valid(e) && e != 0)
return -ERANGE;
return e;
}
int parse_syscall_and_errno(const char *in, char **name, int *error) {
_cleanup_free_ char *n = NULL;
char *p;
int e = -1;
assert(in);
assert(name);
assert(error);
/*
* This parse "syscall:errno" like "uname:EILSEQ", "@sync:255".
* If errno is omitted, then error is set to -1.
* Empty syscall name is not allowed.
* Here, we do not check that the syscall name is valid or not.
*/
p = strchr(in, ':');
if (p) {
e = parse_errno(p + 1);
if (e < 0)
return e;
n = strndup(in, p - in);
} else
n = strdup(in);
if (!n)
return -ENOMEM;
if (isempty(n))
return -EINVAL;
*error = e;
*name = TAKE_PTR(n);
return 0;
}
char *format_bytes(char *buf, size_t l, uint64_t t) {
unsigned i;
/* This only does IEC units so far */
static const struct {
const char *suffix;
uint64_t factor;
} table[] = {
{ "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
{ "M", UINT64_C(1024)*UINT64_C(1024) },
{ "K", UINT64_C(1024) },
};
if (t == (uint64_t) -1)
return NULL;
for (i = 0; i < ELEMENTSOF(table); i++) {
if (t >= table[i].factor) {
snprintf(buf, l,
"%" PRIu64 ".%" PRIu64 "%s",
t / table[i].factor,
((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10),
table[i].suffix);
goto finish;
}
}
snprintf(buf, l, "%" PRIu64 "B", t);
finish:
buf[l-1] = 0;
return buf;
}
#endif /* NM_IGNORED */
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
char *x = NULL;
unsigned long l;
assert(s);
assert(ret_u);
assert(base <= 16);
/* strtoul() is happy to parse negative values, and silently
* converts them to unsigned values without generating an
* error. We want a clean error, hence let's look for the "-"
* prefix on our own, and generate an error. But let's do so
* only after strtoul() validated that the string is clean
* otherwise, so that we return EINVAL preferably over
* ERANGE. */
s += strspn(s, WHITESPACE);
errno = 0;
l = strtoul(s, &x, base);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
if ((unsigned long) (unsigned) l != l)
return -ERANGE;
*ret_u = (unsigned) l;
return 0;
}
int safe_atoi(const char *s, int *ret_i) {
char *x = NULL;
long l;
assert(s);
assert(ret_i);
errno = 0;
l = strtol(s, &x, 0);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
if ((long) (int) l != l)
return -ERANGE;
*ret_i = (int) l;
return 0;
}
int safe_atollu(const char *s, long long unsigned *ret_llu) {
char *x = NULL;
unsigned long long l;
assert(s);
assert(ret_llu);
s += strspn(s, WHITESPACE);
errno = 0;
l = strtoull(s, &x, 0);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
if (*s == '-')
return -ERANGE;
*ret_llu = l;
return 0;
}
int safe_atolli(const char *s, long long int *ret_lli) {
char *x = NULL;
long long l;
assert(s);
assert(ret_lli);
errno = 0;
l = strtoll(s, &x, 0);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
*ret_lli = l;
return 0;
}
int safe_atou8(const char *s, uint8_t *ret) {
char *x = NULL;
unsigned long l;
assert(s);
assert(ret);
s += strspn(s, WHITESPACE);
errno = 0;
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
if ((unsigned long) (uint8_t) l != l)
return -ERANGE;
*ret = (uint8_t) l;
return 0;
}
int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) {
char *x = NULL;
unsigned long l;
assert(s);
assert(ret);
assert(base <= 16);
s += strspn(s, WHITESPACE);
errno = 0;
l = strtoul(s, &x, base);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
if ((unsigned long) (uint16_t) l != l)
return -ERANGE;
*ret = (uint16_t) l;
return 0;
}
int safe_atoi16(const char *s, int16_t *ret) {
char *x = NULL;
long l;
assert(s);
assert(ret);
errno = 0;
l = strtol(s, &x, 0);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
if ((long) (int16_t) l != l)
return -ERANGE;
*ret = (int16_t) l;
return 0;
}
#if 0 /* NM_IGNORED */
int safe_atod(const char *s, double *ret_d) {
_cleanup_(freelocalep) locale_t loc = (locale_t) 0;
char *x = NULL;
double d = 0;
assert(s);
assert(ret_d);
loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
if (loc == (locale_t) 0)
return -errno;
errno = 0;
d = strtod_l(s, &x, loc);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
return -EINVAL;
*ret_d = (double) d;
return 0;
}
int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
size_t i;
unsigned val = 0;
const char *s;
s = *p;
/* accept any number of digits, strtoull is limited to 19 */
for (i=0; i < digits; i++,s++) {
if (*s < '0' || *s > '9') {
if (i == 0)
return -EINVAL;
/* too few digits, pad with 0 */
for (; i < digits; i++)
val *= 10;
break;
}
val *= 10;
val += *s - '0';
}
/* maybe round up */
if (*s >= '5' && *s <= '9')
val++;
s += strspn(s, DIGITS);
*p = s;
*res = val;
return 0;
}
int parse_percent_unbounded(const char *p) {
const char *pc, *n;
int r, v;
pc = endswith(p, "%");
if (!pc)
return -EINVAL;
n = strndupa(p, pc - p);
r = safe_atoi(n, &v);
if (r < 0)
return r;
if (v < 0)
return -ERANGE;
return v;
}
int parse_percent(const char *p) {
int v;
v = parse_percent_unbounded(p);
if (v > 100)
return -ERANGE;
return v;
}
int parse_permille_unbounded(const char *p) {
const char *pc, *pm, *dot, *n;
int r, q, v;
pm = endswith(p, "‰");
if (pm) {
n = strndupa(p, pm - p);
r = safe_atoi(n, &v);
if (r < 0)
return r;
if (v < 0)
return -ERANGE;
} else {
pc = endswith(p, "%");
if (!pc)
return -EINVAL;
dot = memchr(p, '.', pc - p);
if (dot) {
if (dot + 2 != pc)
return -EINVAL;
if (dot[1] < '0' || dot[1] > '9')
return -EINVAL;
q = dot[1] - '0';
n = strndupa(p, dot - p);
} else {
q = 0;
n = strndupa(p, pc - p);
}
r = safe_atoi(n, &v);
if (r < 0)
return r;
if (v < 0)
return -ERANGE;
if (v > (INT_MAX - q) / 10)
return -ERANGE;
v = v * 10 + q;
}
return v;
}
int parse_permille(const char *p) {
int v;
v = parse_permille_unbounded(p);
if (v > 1000)
return -ERANGE;
return v;
}
int parse_nice(const char *p, int *ret) {
int n, r;
r = safe_atoi(p, &n);
if (r < 0)
return r;
if (!nice_is_valid(n))
return -ERANGE;
*ret = n;
return 0;
}
int parse_ip_port(const char *s, uint16_t *ret) {
uint16_t l;
int r;
r = safe_atou16(s, &l);
if (r < 0)
return r;
if (l == 0)
return -EINVAL;
*ret = (uint16_t) l;
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;
size_t n;
int r;
n = strspn(s, DIGITS);
if (n == 0)
return -EINVAL;
if (s[n] != ':')
return -EINVAL;
major = strndupa(s, n);
r = safe_atou(major, &x);
if (r < 0)
return r;
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;
}
int parse_oom_score_adjust(const char *s, int *ret) {
int r, v;
assert(s);
assert(ret);
r = safe_atoi(s, &v);
if (r < 0)
return r;
if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX)
return -ERANGE;
*ret = v;
return 0;
}
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,120 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include "macro.h"
#define MODE_INVALID ((mode_t) -1)
int parse_boolean(const char *v) _pure_;
int parse_dev(const char *s, dev_t *ret);
int parse_pid(const char *s, pid_t* ret_pid);
int parse_mode(const char *s, mode_t *ret);
int parse_ifindex(const char *s, int *ret);
int parse_mtu(int family, const char *s, uint32_t *ret);
int parse_size(const char *t, uint64_t base, uint64_t *size);
int parse_range(const char *t, unsigned *lower, unsigned *upper);
int parse_errno(const char *t);
int parse_syscall_and_errno(const char *in, char **name, int *error);
#define FORMAT_BYTES_MAX 8
char *format_bytes(char *buf, size_t l, uint64_t t);
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
static inline int safe_atou(const char *s, unsigned *ret_u) {
return safe_atou_full(s, 0, ret_u);
}
int safe_atoi(const char *s, int *ret_i);
int safe_atollu(const char *s, unsigned long long *ret_u);
int safe_atolli(const char *s, long long int *ret_i);
int safe_atou8(const char *s, uint8_t *ret);
int safe_atou16_full(const char *s, unsigned base, uint16_t *ret);
static inline int safe_atou16(const char *s, uint16_t *ret) {
return safe_atou16_full(s, 0, ret);
}
static inline int safe_atoux16(const char *s, uint16_t *ret) {
return safe_atou16_full(s, 16, ret);
}
int safe_atoi16(const char *s, int16_t *ret);
static inline int safe_atou32(const char *s, uint32_t *ret_u) {
assert_cc(sizeof(uint32_t) == sizeof(unsigned));
return safe_atou(s, (unsigned*) ret_u);
}
static inline int safe_atoi32(const char *s, int32_t *ret_i) {
assert_cc(sizeof(int32_t) == sizeof(int));
return safe_atoi(s, (int*) ret_i);
}
static inline int safe_atou64(const char *s, uint64_t *ret_u) {
assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
return safe_atollu(s, (unsigned long long*) ret_u);
}
static inline int safe_atoi64(const char *s, int64_t *ret_i) {
assert_cc(sizeof(int64_t) == sizeof(long long int));
return safe_atolli(s, (long long int*) ret_i);
}
#if LONG_MAX == INT_MAX
static inline int safe_atolu(const char *s, unsigned long *ret_u) {
assert_cc(sizeof(unsigned long) == sizeof(unsigned));
return safe_atou(s, (unsigned*) ret_u);
}
static inline int safe_atoli(const char *s, long int *ret_u) {
assert_cc(sizeof(long int) == sizeof(int));
return safe_atoi(s, (int*) ret_u);
}
#else
static inline int safe_atolu(const char *s, unsigned long *ret_u) {
assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
return safe_atollu(s, (unsigned long long*) ret_u);
}
static inline int safe_atoli(const char *s, long int *ret_u) {
assert_cc(sizeof(long int) == sizeof(long long int));
return safe_atolli(s, (long long int*) ret_u);
}
#endif
#if SIZE_MAX == UINT_MAX
static inline int safe_atozu(const char *s, size_t *ret_u) {
assert_cc(sizeof(size_t) == sizeof(unsigned));
return safe_atou(s, (unsigned *) ret_u);
}
#else
static inline int safe_atozu(const char *s, size_t *ret_u) {
assert_cc(sizeof(size_t) == sizeof(long unsigned));
return safe_atolu(s, ret_u);
}
#endif
int safe_atod(const char *s, double *ret_d);
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
int parse_percent_unbounded(const char *p);
int parse_percent(const char *p);
int parse_permille_unbounded(const char *p);
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);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,192 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <alloca.h>
#include <stdbool.h>
#include <stddef.h>
#include "macro.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
#if 0 /* NM_IGNORED */
#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin"
#define PATH_SPLIT_SBIN_BIN_NULSTR(x) x "sbin\0" x "bin\0"
#define PATH_NORMAL_SBIN_BIN(x) x "bin"
#define PATH_NORMAL_SBIN_BIN_NULSTR(x) x "bin\0"
#if HAVE_SPLIT_BIN
# define PATH_SBIN_BIN(x) PATH_SPLIT_SBIN_BIN(x)
# define PATH_SBIN_BIN_NULSTR(x) PATH_SPLIT_SBIN_BIN_NULSTR(x)
#else
# define PATH_SBIN_BIN(x) PATH_NORMAL_SBIN_BIN(x)
# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x)
#endif
#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
#define DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/")
#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/")
#define DEFAULT_PATH_SPLIT_USR_NULSTR DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/")
#define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/")
#if HAVE_SPLIT_USR
# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
# define DEFAULT_PATH_NULSTR DEFAULT_PATH_SPLIT_USR_NULSTR
#else
# define DEFAULT_PATH DEFAULT_PATH_NORMAL
# define DEFAULT_PATH_NULSTR DEFAULT_PATH_NORMAL_NULSTR
#endif
#endif /* NM_IGNORED */
bool is_path(const char *p) _pure_;
int path_split_and_make_absolute(const char *p, char ***ret);
bool path_is_absolute(const char *p) _pure_;
char* path_make_absolute(const char *p, const char *prefix);
int safe_getcwd(char **ret);
int path_make_absolute_cwd(const char *p, char **ret);
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
char* path_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_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) {
return !!a == !!b && (!a || path_equal(a, b));
}
/* Note: the search terminates on the first NULL item. */
#define PATH_IN_SET(p, ...) \
({ \
char **_s; \
bool _found = false; \
STRV_FOREACH(_s, STRV_MAKE(__VA_ARGS__)) \
if (path_equal(p, *_s)) { \
_found = true; \
break; \
} \
_found; \
})
#define PATH_STARTSWITH_SET(p, ...) \
({ \
const char *_p = (p); \
char *_found = NULL, **_i; \
STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
_found = path_startswith(_p, *_i); \
if (_found) \
break; \
} \
_found; \
})
int path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *root);
char** path_strv_resolve_uniq(char **l, const char *root);
int find_binary(const char *name, char **filename);
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
int fsck_exists(const char *fstype);
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), '/'))
/* 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), '/'))
char *prefix_root(const char *root, const char *path);
/* Similar to prefix_root(), but returns an alloca() buffer, or
* possibly a const pointer into the path parameter */
#define prefix_roota(root, path) \
({ \
const char* _path = (path), *_root = (root), *_ret; \
char *_p, *_n; \
size_t _l; \
while (_path[0] == '/' && _path[1] == '/') \
_path ++; \
if (empty_or_root(_root)) \
_ret = _path; \
else { \
_l = strlen(_root) + 1 + strlen(_path) + 1; \
_n = alloca(_l); \
_p = stpcpy(_n, _root); \
while (_p > _n && _p[-1] == '/') \
_p--; \
if (_path[0] != '/') \
*(_p++) = '/'; \
strcpy(_p, _path); \
_ret = _n; \
} \
_ret; \
})
int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg);
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_;
bool path_is_normalized(const char *p) _pure_;
char *file_in_same_dir(const char *path, const char *filename);
bool hidden_or_backup_file(const char *filename) _pure_;
bool is_device_path(const char *path);
bool valid_device_node_path(const char *path);
bool valid_device_allow_pattern(const char *path);
int systemd_installation_has_version(const char *root, unsigned minimal_version);
bool dot_or_dot_dot(const char *path);
static inline const char *skip_dev_prefix(const char *p) {
const char *e;
/* Drop any /dev prefix if there is any */
e = path_startswith(p, "/dev/");
return e ?: p;
}
bool empty_or_root(const char *root);
static inline const char *empty_to_root(const char *path) {
return isempty(path) ? "/" : path;
}
enum {
PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */
PATH_CHECK_ABSOLUTE = 1 << 1,
PATH_CHECK_RELATIVE = 1 << 2,
};
int path_simplify_and_warn(char *path, unsigned flag, const char *unit, const char *filename, unsigned line, const char *lvalue);

View File

@@ -0,0 +1,303 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Priority Queue
* The prioq object implements a priority queue. That is, it orders objects by
* their priority and allows O(1) access to the object with the highest
* priority. Insertion and removal are Θ(log n). Optionally, the caller can
* provide a pointer to an index which will be kept up-to-date by the prioq.
*
* The underlying algorithm used in this implementation is a Heap.
*/
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <stdlib.h>
#include "alloc-util.h"
#include "hashmap.h"
#include "prioq.h"
struct prioq_item {
void *data;
unsigned *idx;
};
struct Prioq {
compare_func_t compare_func;
unsigned n_items, n_allocated;
struct prioq_item *items;
};
Prioq *prioq_new(compare_func_t compare_func) {
Prioq *q;
q = new(Prioq, 1);
if (!q)
return q;
*q = (Prioq) {
.compare_func = compare_func,
};
return q;
}
Prioq* prioq_free(Prioq *q) {
if (!q)
return NULL;
free(q->items);
return mfree(q);
}
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
assert(q);
if (*q)
return 0;
*q = prioq_new(compare_func);
if (!*q)
return -ENOMEM;
return 0;
}
static void swap(Prioq *q, unsigned j, unsigned k) {
assert(q);
assert(j < q->n_items);
assert(k < q->n_items);
assert(!q->items[j].idx || *(q->items[j].idx) == j);
assert(!q->items[k].idx || *(q->items[k].idx) == k);
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;
if (q->items[k].idx)
*q->items[k].idx = k;
}
static unsigned shuffle_up(Prioq *q, unsigned idx) {
assert(q);
assert(idx < q->n_items);
while (idx > 0) {
unsigned k;
k = (idx-1)/2;
if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0)
break;
swap(q, idx, k);
idx = k;
}
return idx;
}
static unsigned shuffle_down(Prioq *q, unsigned idx) {
assert(q);
for (;;) {
unsigned j, k, s;
k = (idx+1)*2; /* right child */
j = k-1; /* left child */
if (j >= q->n_items)
break;
if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
/* So our left child is smaller than we are, let's
* remember this fact */
s = j;
else
s = idx;
if (k < q->n_items &&
q->compare_func(q->items[k].data, q->items[s].data) < 0)
/* So our right child is smaller than we are, let's
* remember this fact */
s = k;
/* s now points to the smallest of the three items */
if (s == idx)
/* No swap necessary, we're done */
break;
swap(q, idx, s);
idx = s;
}
return idx;
}
int prioq_put(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
assert(q);
if (q->n_items >= q->n_allocated) {
unsigned n;
struct prioq_item *j;
n = MAX((q->n_items+1) * 2, 16u);
j = reallocarray(q->items, n, sizeof(struct prioq_item));
if (!j)
return -ENOMEM;
q->items = j;
q->n_allocated = n;
}
k = q->n_items++;
i = q->items + k;
i->data = data;
i->idx = idx;
if (idx)
*idx = k;
shuffle_up(q, k);
return 0;
}
static void remove_item(Prioq *q, struct prioq_item *i) {
struct prioq_item *l;
assert(q);
assert(i);
l = q->items + q->n_items - 1;
if (i == l)
/* Last entry, let's just remove it */
q->n_items--;
else {
unsigned k;
/* Not last entry, let's replace the last entry with
* this one, and reshuffle */
k = i - q->items;
i->data = l->data;
i->idx = l->idx;
if (i->idx)
*i->idx = k;
q->n_items--;
k = shuffle_down(q, k);
shuffle_up(q, k);
}
}
_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
assert(q);
if (q->n_items <= 0)
return NULL;
if (idx) {
if (*idx == PRIOQ_IDX_NULL ||
*idx >= q->n_items)
return NULL;
i = q->items + *idx;
if (i->data != data)
return NULL;
return i;
} else {
for (i = q->items; i < q->items + q->n_items; i++)
if (i->data == data)
return i;
return NULL;
}
}
int prioq_remove(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
if (!q)
return 0;
i = find_item(q, data, idx);
if (!i)
return 0;
remove_item(q, i);
return 1;
}
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
assert(q);
i = find_item(q, data, idx);
if (!i)
return 0;
k = i - q->items;
k = shuffle_down(q, k);
shuffle_up(q, k);
return 1;
}
void *prioq_peek(Prioq *q) {
if (!q)
return NULL;
if (q->n_items <= 0)
return NULL;
return q->items[0].data;
}
void *prioq_pop(Prioq *q) {
void *data;
if (!q)
return NULL;
if (q->n_items <= 0)
return NULL;
data = q->items[0].data;
remove_item(q, q->items);
return data;
}
unsigned prioq_size(Prioq *q) {
if (!q)
return 0;
return q->n_items;
}
bool prioq_isempty(Prioq *q) {
if (!q)
return true;
return q->n_items <= 0;
}

View File

@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include "hashmap.h"
#include "macro.h"
typedef struct Prioq Prioq;
#define PRIOQ_IDX_NULL ((unsigned) -1)
Prioq *prioq_new(compare_func_t compare);
Prioq *prioq_free(Prioq *q);
DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free);
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
int prioq_put(Prioq *q, void *data, unsigned *idx);
int prioq_remove(Prioq *q, void *data, unsigned *idx);
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
void *prioq_peek(Prioq *q) _pure_;
void *prioq_pop(Prioq *q);
unsigned prioq_size(Prioq *q) _pure_;
bool prioq_isempty(Prioq *q) _pure_;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,196 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <alloca.h>
#include <errno.h>
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/types.h>
#include "format-util.h"
#include "ioprio.h"
#include "macro.h"
#include "time-util.h"
#define procfs_file_alloca(pid, field) \
({ \
pid_t _pid_ = (pid); \
const char *_r_; \
if (_pid_ == 0) { \
_r_ = ("/proc/self/" field); \
} else { \
_r_ = alloca(STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
} \
_r_; \
})
int get_process_state(pid_t pid);
int get_process_comm(pid_t pid, char **name);
int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
int get_process_exe(pid_t pid, char **name);
int get_process_uid(pid_t pid, uid_t *uid);
int get_process_gid(pid_t pid, gid_t *gid);
int get_process_capeff(pid_t pid, char **capeff);
int get_process_cwd(pid_t pid, char **cwd);
int get_process_root(pid_t pid, char **root);
int get_process_environ(pid_t pid, char **environ);
int get_process_ppid(pid_t pid, pid_t *ppid);
int wait_for_terminate(pid_t pid, siginfo_t *status);
typedef enum WaitFlags {
WAIT_LOG_ABNORMAL = 1 << 0,
WAIT_LOG_NON_ZERO_EXIT_STATUS = 1 << 1,
/* A shortcut for requesting the most complete logging */
WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
} WaitFlags;
int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags);
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
void sigkill_wait(pid_t pid);
void sigkill_waitp(pid_t *pid);
void sigterm_wait(pid_t pid);
int kill_and_sigcont(pid_t pid, int sig);
int rename_process(const char name[]);
int is_kernel_thread(pid_t pid);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
bool pid_is_alive(pid_t pid);
bool pid_is_unwaited(pid_t pid);
int pid_from_same_root_fs(pid_t pid);
bool is_main_thread(void);
_noreturn_ void freeze(void);
bool oom_score_adjust_is_valid(int oa);
#ifndef PERSONALITY_INVALID
/* personality(7) documents that 0xffffffffUL is used for querying the
* current personality, hence let's use that here as error
* indicator. */
#define PERSONALITY_INVALID 0xffffffffLU
#endif
unsigned long personality_from_string(const char *p);
const char *personality_to_string(unsigned long);
int safe_personality(unsigned long p);
int opinionated_personality(unsigned long *ret);
int ioprio_class_to_string_alloc(int i, char **s);
int ioprio_class_from_string(const char *s);
const char *sigchld_code_to_string(int i) _const_;
int sigchld_code_from_string(const char *s) _pure_;
int sched_policy_to_string_alloc(int i, char **s);
int sched_policy_from_string(const char *s);
static inline pid_t PTR_TO_PID(const void *p) {
return (pid_t) ((uintptr_t) p);
}
static inline void* PID_TO_PTR(pid_t pid) {
return (void*) ((uintptr_t) pid);
}
void valgrind_summary_hack(void);
int pid_compare_func(const pid_t *a, const pid_t *b);
#if 0 /* NM_IGNORED */
static inline bool nice_is_valid(int n) {
return n >= PRIO_MIN && n < PRIO_MAX;
}
static inline bool sched_policy_is_valid(int i) {
return IN_SET(i, SCHED_OTHER, SCHED_BATCH, SCHED_IDLE, SCHED_FIFO, SCHED_RR);
}
static inline bool sched_priority_is_valid(int i) {
return i >= 0 && i <= sched_get_priority_max(SCHED_RR);
}
static inline bool ioprio_class_is_valid(int i) {
return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE);
}
static inline bool ioprio_priority_is_valid(int i) {
return i >= 0 && i < IOPRIO_BE_NR;
}
static inline bool pid_is_valid(pid_t p) {
return p > 0;
}
#endif /* NM_IGNORED */
int ioprio_parse_priority(const char *s, int *ret);
pid_t getpid_cached(void);
void reset_cached_pid(void);
int must_be_root(void);
typedef enum ForkFlags {
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */
FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */
FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */
FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */
FORK_WAIT = 1 << 6, /* Wait until child exited */
FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */
FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */
FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
return safe_fork_full(name, NULL, 0, flags, ret_pid);
}
int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...) _sentinel_;
int set_oom_score_adjust(int value);
#if SIZEOF_PID_T == 4
/* The highest possibly (theoretic) pid_t value on this architecture. */
#define PID_T_MAX ((pid_t) INT32_MAX)
/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value
* the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the
* upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by
* 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since
* these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at
* least to define them here too. */
#define TASKS_MAX 4194303U
#elif SIZEOF_PID_T == 2
#define PID_T_MAX ((pid_t) INT16_MAX)
#define TASKS_MAX 32767U
#else
#error "Unknown pid_t size"
#endif
assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX)
/* Like TAKE_PTR() but for child PIDs, resetting them to 0 */
#define TAKE_PID(pid) \
({ \
pid_t _pid_ = (pid); \
(pid) = 0; \
_pid_; \
})

View File

@@ -0,0 +1,274 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#if defined(__i386__) || defined(__x86_64__)
#include <cpuid.h>
#endif
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#if HAVE_SYS_AUXV_H
# include <sys/auxv.h>
#endif
#if USE_SYS_RANDOM_H
# include <sys/random.h>
#else
# include <linux/random.h>
#endif
#include "fd-util.h"
#include "io-util.h"
#include "missing.h"
#include "random-util.h"
#include "time-util.h"
#if HAS_FEATURE_MEMORY_SANITIZER
#include <sanitizer/msan_interface.h>
#endif
int rdrand(unsigned long *ret) {
#if defined(__i386__) || defined(__x86_64__)
static int have_rdrand = -1;
unsigned char err;
if (have_rdrand < 0) {
uint32_t eax, ebx, ecx, edx;
/* Check if RDRAND is supported by the CPU */
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) {
have_rdrand = false;
return -EOPNOTSUPP;
}
have_rdrand = !!(ecx & (1U << 30));
}
if (have_rdrand == 0)
return -EOPNOTSUPP;
asm volatile("rdrand %0;"
"setc %1"
: "=r" (*ret),
"=qm" (err));
#if HAS_FEATURE_MEMORY_SANITIZER
__msan_unpoison(&err, sizeof(err));
#endif
if (!err)
return -EAGAIN;
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
static int have_syscall = -1;
_cleanup_close_ int fd = -1;
bool got_some = false;
int r;
/* Gathers some randomness from the kernel (or the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't
* block, unless the RANDOM_BLOCK flag is set. If RANDOM_DONT_DRAIN is set, an error is returned if the random
* pool is not initialized. Otherwise it will always return some data from the kernel, regardless of whether
* the random pool is fully initialized or not. */
if (n == 0)
return 0;
if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND))
/* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is not
* required, as we don't trust it (who does?). Note that we only do a single iteration of RDRAND here,
* even though the Intel docs suggest calling this in a tight loop of 10 invocations or so. That's
* because we don't really care about the quality here. We generally prefer using RDRAND if the caller
* allows us too, since this way we won't drain the kernel randomness pool if we don't need it, as the
* pool's entropy is scarce. */
for (;;) {
unsigned long u;
size_t m;
if (rdrand(&u) < 0) {
if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
/* Fill in the remaining bytes using pseudo-random values */
pseudo_random_bytes(p, n);
return 0;
}
/* OK, this didn't work, let's go to getrandom() + /dev/urandom instead */
break;
}
m = MIN(sizeof(u), n);
memcpy(p, &u, m);
p = (uint8_t*) p + m;
n -= m;
if (n == 0)
return 0; /* Yay, success! */
got_some = true;
}
/* Use the getrandom() syscall unless we know we don't have it. */
if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
for (;;) {
#if !HAVE_GETRANDOM
/* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround.
* If you don't compile against a libc that provides getrandom(), you don't get it. */
r = -1;
errno = ENOSYS;
#else
r = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK);
#endif
if (r > 0) {
have_syscall = true;
if ((size_t) r == n)
return 0; /* Yay, success! */
assert((size_t) r < n);
p = (uint8_t*) p + r;
n -= r;
if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
/* Fill in the remaining bytes using pseudo-random values */
pseudo_random_bytes(p, n);
return 0;
}
got_some = true;
/* Hmm, we didn't get enough good data but the caller insists on good data? Then try again */
if (FLAGS_SET(flags, RANDOM_BLOCK))
continue;
/* Fill in the rest with /dev/urandom */
break;
} else if (r == 0) {
have_syscall = true;
return -EIO;
} else if (errno == ENOSYS) {
/* We lack the syscall, continue with reading from /dev/urandom. */
have_syscall = false;
break;
} else if (errno == EAGAIN) {
/* The kernel has no entropy whatsoever. Let's remember to use the syscall the next
* time again though.
*
* If RANDOM_DONT_DRAIN is set, return an error so that random_bytes() can produce some
* pseudo-random bytes instead. Otherwise, fall back to /dev/urandom, which we know is empty,
* but the kernel will produce some bytes for us on a best-effort basis. */
have_syscall = true;
if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
/* Fill in the remaining bytes using pseudorandom values */
pseudo_random_bytes(p, n);
return 0;
}
if (FLAGS_SET(flags, RANDOM_DONT_DRAIN))
return -ENODATA;
/* Use /dev/urandom instead */
break;
} else
return -errno;
}
}
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return errno == ENOENT ? -ENOSYS : -errno;
return loop_read_exact(fd, p, n, true);
}
void initialize_srand(void) {
static bool srand_called = false;
unsigned x;
#if HAVE_SYS_AUXV_H
const void *auxv;
#endif
unsigned long k;
if (srand_called)
return;
#if HAVE_SYS_AUXV_H
/* The kernel provides us with 16 bytes of entropy in auxv, so let's
* try to make use of that to seed the pseudo-random generator. It's
* better than nothing... */
auxv = (const void*) getauxval(AT_RANDOM);
if (auxv) {
assert_cc(sizeof(x) <= 16);
memcpy(&x, auxv, sizeof(x));
} else
#endif
x = 0;
x ^= (unsigned) now(CLOCK_REALTIME);
x ^= (unsigned) gettid();
if (rdrand(&k) >= 0)
x ^= (unsigned) k;
srand(x);
srand_called = true;
}
/* INT_MAX gives us only 31 bits, so use 24 out of that. */
#if RAND_MAX >= INT_MAX
# define RAND_STEP 3
#else
/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */
# define RAND_STEP 1
#endif
void pseudo_random_bytes(void *p, size_t n) {
uint8_t *q;
initialize_srand();
for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) {
unsigned rr;
rr = (unsigned) rand();
#if RAND_STEP >= 3
if ((size_t) (q - (uint8_t*) p + 2) < n)
q[2] = rr >> 16;
#endif
#if RAND_STEP >= 2
if ((size_t) (q - (uint8_t*) p + 1) < n)
q[1] = rr >> 8;
#endif
q[0] = rr;
}
}
void random_bytes(void *p, size_t n) {
if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_DONT_DRAIN|RANDOM_ALLOW_RDRAND) >= 0)
return;
/* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */
pseudo_random_bytes(p, n);
}

View File

@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef enum RandomFlags {
RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */
RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */
RANDOM_DONT_DRAIN = 1 << 2, /* If we can't get any randomness at all, return early with -EAGAIN */
RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */
} RandomFlags;
int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled upwith pseudo random, if not enough is available */
void pseudo_random_bytes(void *p, size_t n); /* returns only pseudo-randommess (but possibly seeded from something better) */
void random_bytes(void *p, size_t n); /* returns genuine randomness if cheaply available, and pseudo randomness if not. */
void initialize_srand(void);
static inline uint64_t random_u64(void) {
uint64_t u;
random_bytes(&u, sizeof(u));
return u;
}
static inline uint32_t random_u32(void) {
uint32_t u;
random_bytes(&u, sizeof(u));
return u;
}
int rdrand(unsigned long *ret);

View File

@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
/* A type-safe atomic refcounter.
*
* DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */
typedef struct {
volatile unsigned _value;
} RefCount;
#define REFCNT_GET(r) ((r)._value)
#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1))
#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1))
#define REFCNT_INIT ((RefCount) { ._value = 1 })
#define _DEFINE_ATOMIC_REF_FUNC(type, name, scope) \
scope type *name##_ref(type *p) { \
if (!p) \
return NULL; \
\
assert_se(REFCNT_INC(p->n_ref) >= 2); \
return p; \
}
#define _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func, scope) \
scope type *name##_unref(type *p) { \
if (!p) \
return NULL; \
\
if (REFCNT_DEC(p->n_ref) > 0) \
return NULL; \
\
return free_func(p); \
}
#define DEFINE_ATOMIC_REF_FUNC(type, name) \
_DEFINE_ATOMIC_REF_FUNC(type, name,)
#define DEFINE_PUBLIC_ATOMIC_REF_FUNC(type, name) \
_DEFINE_ATOMIC_REF_FUNC(type, name, _public_)
#define DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func) \
_DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func,)
#define DEFINE_PUBLIC_ATOMIC_UNREF_FUNC(type, name, free_func) \
_DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func, _public_)
#define DEFINE_ATOMIC_REF_UNREF_FUNC(type, name, free_func) \
DEFINE_ATOMIC_REF_FUNC(type, name); \
DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func);
#define DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(type, name, free_func) \
DEFINE_PUBLIC_ATOMIC_REF_FUNC(type, name); \
DEFINE_PUBLIC_ATOMIC_UNREF_FUNC(type, name, free_func);

View File

@@ -0,0 +1,130 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "extract-word.h"
#include "hashmap.h"
#include "macro.h"
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) {
return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL);
}
static inline Set *set_free_free(Set *s) {
return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL);
}
/* no set_free_free_free */
static inline Set *set_copy(Set *s) {
return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
}
int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int set_put(Set *s, const void *key);
/* no set_update */
/* no set_replace */
static inline void *set_get(Set *s, void *key) {
return internal_hashmap_get(HASHMAP_BASE(s), key);
}
/* no set_get2 */
static inline bool set_contains(Set *s, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(s), key);
}
static inline void *set_remove(Set *s, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(s), key);
}
/* no set_remove2 */
/* no set_remove_value */
int set_remove_and_put(Set *s, const void *old_key, const void *new_key);
/* no set_remove_and_replace */
int set_merge(Set *s, Set *other);
static inline int set_reserve(Set *h, unsigned entries_add) {
return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
}
static inline int set_move(Set *s, Set *other) {
return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
}
static inline int set_move_one(Set *s, Set *other, const void *key) {
return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
}
static inline unsigned set_size(Set *s) {
return internal_hashmap_size(HASHMAP_BASE(s));
}
static inline bool set_isempty(Set *s) {
return set_size(s) == 0;
}
static inline unsigned set_buckets(Set *s) {
return internal_hashmap_buckets(HASHMAP_BASE(s));
}
bool set_iterate(Set *s, Iterator *i, void **value);
static inline void set_clear(Set *s) {
internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
}
static inline void set_clear_free(Set *s) {
internal_hashmap_clear(HASHMAP_BASE(s), free, NULL);
}
/* no set_clear_free_free */
static inline void *set_steal_first(Set *s) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
}
#define set_clear_with_destructor(_s, _f) \
({ \
void *_item; \
while ((_item = set_steal_first(_s))) \
_f(_item); \
})
#define set_free_with_destructor(_s, _f) \
({ \
set_clear_with_destructor(_s, _f); \
set_free(_s); \
})
/* no set_steal_first_key */
/* no set_first_key */
static inline void *set_first(Set *s) {
return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), false, NULL);
}
/* no set_next */
static inline char **set_get_strv(Set *s) {
return internal_hashmap_get_strv(HASHMAP_BASE(s));
}
int set_consume(Set *s, void *value);
int set_put_strdup(Set *s, const char *p);
int set_put_strdupv(Set *s, char **l);
int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags);
#define SET_FOREACH(e, s, i) \
for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); )
#define SET_FOREACH_MOVE(e, d, s) \
for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); )
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
#define _cleanup_set_free_ _cleanup_(set_freep)
#define _cleanup_set_free_free_ _cleanup_(set_free_freep)

View File

@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <signal.h>
#include "macro.h"
int reset_all_signal_handlers(void);
int reset_signal_mask(void);
int ignore_signals(int sig, ...);
int default_signals(int sig, ...);
int sigaction_many(const struct sigaction *sa, ...);
int sigset_add_many(sigset_t *ss, ...);
int sigprocmask_many(int how, sigset_t *old, ...);
const char *signal_to_string(int i) _const_;
int signal_from_string(const char *s) _pure_;
void nop_signal_handler(int sig);
static inline void block_signals_reset(sigset_t *ss) {
assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0);
}
#define BLOCK_SIGNALS(...) \
_cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \
sigset_t _t; \
assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \
_t; \
})
static inline bool SIGNAL_VALID(int signo) {
return signo > 0 && signo < _NSIG;
}
static inline const char* signal_to_string_with_check(int n) {
if (!SIGNAL_VALID(n))
return NULL;
return signal_to_string(n);
}

View File

@@ -0,0 +1,58 @@
#pragma once
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#if 0 /* NM_IGNORED */
struct siphash {
uint64_t v0;
uint64_t v1;
uint64_t v2;
uint64_t v3;
uint64_t padding;
size_t inlen;
};
#else /* NM_IGNORED */
struct siphash {
CSipHash _csiphash;
};
static inline void
siphash24_init (struct siphash *state, const uint8_t k[16])
{
c_siphash_init ((CSipHash *) state, k);
}
static inline void
siphash24_compress (const void *in, size_t inlen, struct siphash *state)
{
c_siphash_append ((CSipHash *) state, in, inlen);
}
static inline uint64_t
siphash24_finalize (struct siphash *state)
{
return c_siphash_finalize ((CSipHash *) state);
}
static inline uint64_t
siphash24 (const void *in, size_t inlen, const uint8_t k[16])
{
return c_siphash_hash (k, in, inlen);
}
#endif /* NM_IGNORED */
void siphash24_init(struct siphash *state, const uint8_t k[16]);
void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
uint64_t siphash24_finalize(struct siphash *state);
uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]);
static inline uint64_t siphash24_string(const char *s, const uint8_t k[16]) {
return siphash24(s, strlen(s) + 1, k);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,202 @@
/* 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>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include "macro.h"
#include "missing_socket.h"
#include "sparse-endian.h"
union sockaddr_union {
/* The minimal, abstract version */
struct sockaddr sa;
/* The libc provided version that allocates "enough room" for every protocol */
struct sockaddr_storage storage;
/* Protoctol-specific implementations */
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
struct sockaddr_nl nl;
struct sockaddr_ll ll;
#if 0 /* NM_IGNORED */
struct sockaddr_vm vm;
#endif /* NM_IGNORED */
/* Ensure there is enough space to store Infiniband addresses */
uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)];
/* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path
* component is always followed by at least one NUL byte. */
uint8_t un_buffer[sizeof(struct sockaddr_un) + 1];
};
typedef struct SocketAddress {
union sockaddr_union sockaddr;
/* We store the size here explicitly due to the weird
* sockaddr_un semantics for abstract sockets */
socklen_t size;
/* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
int type;
/* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */
int protocol;
} SocketAddress;
typedef enum SocketAddressBindIPv6Only {
SOCKET_ADDRESS_DEFAULT,
SOCKET_ADDRESS_BOTH,
SOCKET_ADDRESS_IPV6_ONLY,
_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
_SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
} SocketAddressBindIPv6Only;
#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
const char* socket_address_type_to_string(int t) _const_;
int socket_address_type_from_string(const char *s) _pure_;
int socket_address_parse(SocketAddress *a, const char *s);
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
int socket_address_print(const SocketAddress *a, char **p);
int socket_address_verify(const SocketAddress *a, bool strict) _pure_;
int sockaddr_un_unlink(const struct sockaddr_un *sa);
static inline int socket_address_unlink(const SocketAddress *a) {
return socket_address_family(a) == AF_UNIX ? sockaddr_un_unlink(&a->sockaddr.un) : 0;
}
bool socket_address_can_accept(const SocketAddress *a) _pure_;
int socket_address_listen(
const SocketAddress *a,
int flags,
int backlog,
SocketAddressBindIPv6Only only,
const char *bind_to_device,
bool reuse_port,
bool free_bind,
bool transparent,
mode_t directory_mode,
mode_t socket_mode,
const char *label);
int make_socket_fd(int log_level, const char* address, int type, int flags);
bool socket_address_is(const SocketAddress *a, const char *s, int type);
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
bool socket_address_matches_fd(const SocketAddress *a, int fd);
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_;
const char* socket_address_get_path(const SocketAddress *a);
bool socket_ipv6_is_supported(void);
int sockaddr_port(const struct sockaddr *_sa, unsigned *port);
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
int getpeername_pretty(int fd, bool include_port, char **ret);
int getsockname_pretty(int fd, char **ret);
int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *s);
int netlink_family_to_string_alloc(int b, char **s);
int netlink_family_from_string(const char *s) _pure_;
bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b);
int fd_inc_sndbuf(int fd, size_t n);
int fd_inc_rcvbuf(int fd, size_t n);
int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);
bool ifname_valid(const char *p);
bool address_label_valid(const char *p);
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
int getpeergroups(int fd, gid_t **ret);
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
struct iovec *iov, size_t iovlen,
const struct sockaddr *sa, socklen_t len,
int flags);
int send_one_fd_sa(int transport_fd,
int fd,
const struct sockaddr *sa, socklen_t len,
int flags);
#define send_one_fd_iov(transport_fd, fd, iov, iovlen, flags) send_one_fd_iov_sa(transport_fd, fd, iov, iovlen, NULL, 0, flags)
#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags)
ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd);
int receive_one_fd(int transport_fd, int flags);
ssize_t next_datagram_size_fd(int fd);
int flush_accept(int fd);
#define CMSG_FOREACH(cmsg, mh) \
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
/*
* Certain hardware address types (e.g Infiniband) do not fit into sll_addr
* (8 bytes) and run over the structure. This macro returns the correct size that
* must be passed to kernel.
*/
#define SOCKADDR_LL_LEN(sa) \
({ \
const struct sockaddr_ll *_sa = &(sa); \
size_t _mac_len = sizeof(_sa->sll_addr); \
assert(_sa->sll_family == AF_PACKET); \
if (be16toh(_sa->sll_hatype) == ARPHRD_ETHER) \
_mac_len = MAX(_mac_len, (size_t) ETH_ALEN); \
if (be16toh(_sa->sll_hatype) == ARPHRD_INFINIBAND) \
_mac_len = MAX(_mac_len, (size_t) INFINIBAND_ALEN); \
offsetof(struct sockaddr_ll, sll_addr) + _mac_len; \
})
/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
#define SOCKADDR_UN_LEN(sa) \
({ \
const struct sockaddr_un *_sa = &(sa); \
assert(_sa->sun_family == AF_UNIX); \
offsetof(struct sockaddr_un, sun_path) + \
(_sa->sun_path[0] == 0 ? \
1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
})
int socket_ioctl_fd(void);
int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path);
static inline int setsockopt_int(int fd, int level, int optname, int value) {
if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0)
return -errno;
return 0;
}

View File

@@ -0,0 +1,90 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (c) 2012 Josh Triplett <josh@joshtriplett.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include <byteswap.h>
#include <endian.h>
#include <stdint.h>
#ifdef __CHECKER__
#define __sd_bitwise __attribute__((__bitwise__))
#define __sd_force __attribute__((__force__))
#else
#define __sd_bitwise
#define __sd_force
#endif
typedef uint16_t __sd_bitwise le16_t;
typedef uint16_t __sd_bitwise be16_t;
typedef uint32_t __sd_bitwise le32_t;
typedef uint32_t __sd_bitwise be32_t;
typedef uint64_t __sd_bitwise le64_t;
typedef uint64_t __sd_bitwise be64_t;
#undef htobe16
#undef htole16
#undef be16toh
#undef le16toh
#undef htobe32
#undef htole32
#undef be32toh
#undef le32toh
#undef htobe64
#undef htole64
#undef be64toh
#undef le64toh
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define bswap_16_on_le(x) __bswap_16(x)
#define bswap_32_on_le(x) __bswap_32(x)
#define bswap_64_on_le(x) __bswap_64(x)
#define bswap_16_on_be(x) (x)
#define bswap_32_on_be(x) (x)
#define bswap_64_on_be(x) (x)
#elif __BYTE_ORDER == __BIG_ENDIAN
#define bswap_16_on_le(x) (x)
#define bswap_32_on_le(x) (x)
#define bswap_64_on_le(x) (x)
#define bswap_16_on_be(x) __bswap_16(x)
#define bswap_32_on_be(x) __bswap_32(x)
#define bswap_64_on_be(x) __bswap_64(x)
#endif
static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); }
static inline le32_t htole32(uint32_t value) { return (le32_t __sd_force) bswap_32_on_be(value); }
static inline le64_t htole64(uint64_t value) { return (le64_t __sd_force) bswap_64_on_be(value); }
static inline be16_t htobe16(uint16_t value) { return (be16_t __sd_force) bswap_16_on_le(value); }
static inline be32_t htobe32(uint32_t value) { return (be32_t __sd_force) bswap_32_on_le(value); }
static inline be64_t htobe64(uint64_t value) { return (be64_t __sd_force) bswap_64_on_le(value); }
static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __sd_force)value); }
static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __sd_force)value); }
static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __sd_force)value); }
static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __sd_force)value); }
static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __sd_force)value); }
static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __sd_force)value); }
#undef __sd_bitwise
#undef __sd_force

View File

@@ -0,0 +1,425 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/magic.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#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"
#if 0 /* NM_IGNORED */
int is_symlink(const char *path) {
struct stat info;
assert(path);
if (lstat(path, &info) < 0)
return -errno;
return !!S_ISLNK(info.st_mode);
}
int is_dir(const char* path, bool follow) {
struct stat st;
int r;
assert(path);
if (follow)
r = stat(path, &st);
else
r = lstat(path, &st);
if (r < 0)
return -errno;
return !!S_ISDIR(st.st_mode);
}
int is_dir_fd(int fd) {
struct stat st;
if (fstat(fd, &st) < 0)
return -errno;
return !!S_ISDIR(st.st_mode);
}
int is_device_node(const char *path) {
struct stat info;
assert(path);
if (lstat(path, &info) < 0)
return -errno;
return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
}
int dir_is_empty(const char *path) {
_cleanup_closedir_ DIR *d;
struct dirent *de;
d = opendir(path);
if (!d)
return -errno;
FOREACH_DIRENT(de, d, return -errno)
return 0;
return 1;
}
bool null_or_empty(struct stat *st) {
assert(st);
if (S_ISREG(st->st_mode) && st->st_size <= 0)
return true;
/* We don't want to hardcode the major/minor of /dev/null,
* hence we do a simpler "is this a device node?" check. */
if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
return true;
return false;
}
int null_or_empty_path(const char *fn) {
struct stat st;
assert(fn);
if (stat(fn, &st) < 0)
return -errno;
return null_or_empty(&st);
}
int null_or_empty_fd(int fd) {
struct stat st;
assert(fd >= 0);
if (fstat(fd, &st) < 0)
return -errno;
return null_or_empty(&st);
}
int path_is_read_only_fs(const char *path) {
struct statvfs st;
assert(path);
if (statvfs(path, &st) < 0)
return -errno;
if (st.f_flag & ST_RDONLY)
return true;
/* On NFS, statvfs() might not reflect whether we can actually
* write to the remote share. Let's try again with
* access(W_OK) which is more reliable, at least sometimes. */
if (access(path, W_OK) < 0 && errno == EROFS)
return true;
return false;
}
int files_same(const char *filea, const char *fileb, int flags) {
struct stat a, b;
assert(filea);
assert(fileb);
if (fstatat(AT_FDCWD, filea, &a, flags) < 0)
return -errno;
if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
return -errno;
return a.st_dev == b.st_dev &&
a.st_ino == b.st_ino;
}
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
assert(s);
assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
return F_TYPE_EQUAL(s->f_type, magic_value);
}
int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
struct statfs s;
if (fstatfs(fd, &s) < 0)
return -errno;
return is_fs_type(&s, magic_value);
}
int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
_cleanup_close_ int fd = -1;
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
if (fd < 0)
return -errno;
return fd_is_fs_type(fd, magic_value);
}
bool is_temporary_fs(const struct statfs *s) {
return is_fs_type(s, TMPFS_MAGIC) ||
is_fs_type(s, RAMFS_MAGIC);
}
bool is_network_fs(const struct statfs *s) {
return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
is_fs_type(s, CODA_SUPER_MAGIC) ||
is_fs_type(s, NCP_SUPER_MAGIC) ||
is_fs_type(s, NFS_SUPER_MAGIC) ||
is_fs_type(s, SMB_SUPER_MAGIC) ||
is_fs_type(s, V9FS_MAGIC) ||
is_fs_type(s, AFS_SUPER_MAGIC) ||
is_fs_type(s, OCFS2_SUPER_MAGIC);
}
int fd_is_temporary_fs(int fd) {
struct statfs s;
if (fstatfs(fd, &s) < 0)
return -errno;
return is_temporary_fs(&s);
}
int fd_is_network_fs(int fd) {
struct statfs s;
if (fstatfs(fd, &s) < 0)
return -errno;
return is_network_fs(&s);
}
int fd_is_network_ns(int fd) {
struct statfs s;
int r;
/* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice
* way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle
* this somewhat nicely.
*
* This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not
* refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */
if (fstatfs(fd, &s) < 0)
return -errno;
if (!is_fs_type(&s, NSFS_MAGIC)) {
/* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs
* instead. Handle that in a somewhat smart way. */
if (is_fs_type(&s, PROC_SUPER_MAGIC)) {
struct statfs t;
/* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the
* passed fd might refer to a network namespace, but we can't know for sure. In that case,
* return a recognizable error. */
if (statfs("/proc/self/ns/net", &t) < 0)
return -errno;
if (s.f_type == t.f_type)
return -EUCLEAN; /* It's possible, we simply don't know */
}
return 0; /* No! */
}
r = ioctl(fd, NS_GET_NSTYPE);
if (r < 0) {
if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
return -EUCLEAN;
return -errno;
}
return r == CLONE_NEWNET;
}
int path_is_temporary_fs(const char *path) {
_cleanup_close_ int fd = -1;
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
if (fd < 0)
return -errno;
return fd_is_temporary_fs(fd);
}
#endif /* NM_IGNORED */
int stat_verify_regular(const struct stat *st) {
assert(st);
/* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
* code. */
if (S_ISDIR(st->st_mode))
return -EISDIR;
if (S_ISLNK(st->st_mode))
return -ELOOP;
if (!S_ISREG(st->st_mode))
return -EBADFD;
return 0;
}
int fd_verify_regular(int fd) {
struct stat st;
assert(fd >= 0);
if (fstat(fd, &st) < 0)
return -errno;
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 */

View File

@@ -0,0 +1,87 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include "macro.h"
int is_symlink(const char *path);
int is_dir(const char *path, bool follow);
int is_dir_fd(int fd);
int is_device_node(const char *path);
int dir_is_empty(const char *path);
static inline int dir_is_populated(const char *path) {
int r;
r = dir_is_empty(path);
if (r < 0)
return r;
return !r;
}
bool null_or_empty(struct stat *st) _pure_;
int null_or_empty_path(const char *fn);
int null_or_empty_fd(int fd);
int path_is_read_only_fs(const char *path);
int files_same(const char *filea, const char *fileb, int flags);
/* The .f_type field of struct statfs is really weird defined on
* different archs. Let's give its type a name. */
typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t;
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_;
int fd_is_fs_type(int fd, statfs_f_type_t magic_value);
int path_is_fs_type(const char *path, statfs_f_type_t magic_value);
bool is_temporary_fs(const struct statfs *s) _pure_;
bool is_network_fs(const struct statfs *s) _pure_;
int fd_is_temporary_fs(int fd);
int fd_is_network_fs(int fd);
int fd_is_network_ns(int fd);
int path_is_temporary_fs(const char *path);
/* Because statfs.t_type can be int on some architectures, we have to cast
* the const magic to the type, otherwise the compiler warns about
* signed/unsigned comparison, because the magic can be 32 bit unsigned.
*/
#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
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);

View File

@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <printf.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/types.h>
#include "macro.h"
#include "util.h"
#define snprintf_ok(buf, len, fmt, ...) \
((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len))
#define xsprintf(buf, fmt, ...) \
assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough")
#define VA_FORMAT_ADVANCE(format, ap) \
do { \
int _argtypes[128]; \
size_t _i, _k; \
/* See https://github.com/google/sanitizers/issues/992 */ \
if (HAS_FEATURE_MEMORY_SANITIZER) \
zero(_argtypes); \
_k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
assert(_k < ELEMENTSOF(_argtypes)); \
for (_i = 0; _i < _k; _i++) { \
if (_argtypes[_i] & PA_FLAG_PTR) { \
(void) va_arg(ap, void*); \
continue; \
} \
\
switch (_argtypes[_i]) { \
case PA_INT: \
case PA_INT|PA_FLAG_SHORT: \
case PA_CHAR: \
(void) va_arg(ap, int); \
break; \
case PA_INT|PA_FLAG_LONG: \
(void) va_arg(ap, long int); \
break; \
case PA_INT|PA_FLAG_LONG_LONG: \
(void) va_arg(ap, long long int); \
break; \
case PA_WCHAR: \
(void) va_arg(ap, wchar_t); \
break; \
case PA_WSTRING: \
case PA_STRING: \
case PA_POINTER: \
(void) va_arg(ap, void*); \
break; \
case PA_FLOAT: \
case PA_DOUBLE: \
(void) va_arg(ap, double); \
break; \
case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \
(void) va_arg(ap, long double); \
break; \
default: \
assert_not_reached("Unknown format string argument."); \
} \
} \
} while (false)

View File

@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include "string-table.h"
#include "string-util.h"
ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
size_t i;
if (!key)
return -1;
for (i = 0; i < len; ++i)
if (streq_ptr(table[i], key))
return (ssize_t) i;
return -1;
}

View File

@@ -0,0 +1,112 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include "macro.h"
#include "parse-util.h"
#include "string-util.h"
ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
/* For basic lookup tables with strictly enumerated entries */
#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
scope const char *name##_to_string(type i) { \
if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \
return NULL; \
return name##_table[i]; \
}
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
scope type name##_from_string(const char *s) { \
return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
}
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
scope type name##_from_string(const char *s) { \
int b; \
if (!s) \
return -1; \
b = parse_boolean(s); \
if (b == 0) \
return (type) 0; \
else if (b > 0) \
return yes; \
return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
}
#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \
scope int name##_to_string_alloc(type i, char **str) { \
char *s; \
if (i < 0 || i > max) \
return -ERANGE; \
if (i < (type) ELEMENTSOF(name##_table)) { \
s = strdup(name##_table[i]); \
if (!s) \
return -ENOMEM; \
} else { \
if (asprintf(&s, "%i", i) < 0) \
return -ENOMEM; \
} \
*str = s; \
return 0; \
}
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \
scope type name##_from_string(const char *s) { \
type i; \
unsigned u = 0; \
if (!s) \
return (type) -1; \
for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \
if (streq_ptr(name##_table[i], s)) \
return i; \
if (safe_atou(s, &u) >= 0 && u <= max) \
return (type) u; \
return (type) -1; \
} \
#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope)
#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope)
#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,)
/* For string conversions where numbers are also acceptable */
#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static)
#define DUMP_STRING_TABLE(name,type,max) \
do { \
type _k; \
flockfile(stdout); \
for (_k = 0; _k < (max); _k++) { \
const char *_t; \
_t = name##_to_string(_k); \
if (!_t) \
continue; \
fputs_unlocked(_t, stdout); \
fputc_unlocked('\n', stdout); \
} \
funlockfile(stdout); \
} while(false)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,265 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <alloca.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "macro.h"
/* What is interpreted as whitespace? */
#define WHITESPACE " \t\n\r"
#define NEWLINE "\n\r"
#define QUOTES "\"\'"
#define COMMENTS "#;"
#define GLOB_CHARS "*?["
#define DIGITS "0123456789"
#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
#define ALPHANUMERICAL LETTERS DIGITS
#define HEXDIGITS DIGITS "abcdefABCDEF"
#define streq(a,b) (strcmp((a),(b)) == 0)
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
int strcmp_ptr(const char *a, const char *b) _pure_;
static inline bool streq_ptr(const char *a, const char *b) {
return strcmp_ptr(a, b) == 0;
}
static inline const char* strempty(const char *s) {
return s ?: "";
}
static inline const char* strnull(const char *s) {
return s ?: "(null)";
}
static inline const char *strna(const char *s) {
return s ?: "n/a";
}
static inline bool isempty(const char *p) {
return !p || !p[0];
}
static inline const char *empty_to_null(const char *p) {
return isempty(p) ? NULL : p;
}
static inline const char *empty_to_dash(const char *str) {
return isempty(str) ? "-" : str;
}
static inline char *startswith(const char *s, const char *prefix) {
size_t l;
l = strlen(prefix);
if (strncmp(s, prefix, l) == 0)
return (char*) s + l;
return NULL;
}
static inline char *startswith_no_case(const char *s, const char *prefix) {
size_t l;
l = strlen(prefix);
if (strncasecmp(s, prefix, l) == 0)
return (char*) s + l;
return NULL;
}
char *endswith(const char *s, const char *postfix) _pure_;
char *endswith_no_case(const char *s, const char *postfix) _pure_;
char *first_word(const char *s, const char *word) _pure_;
typedef enum SplitFlags {
SPLIT_QUOTES = 0x01 << 0,
SPLIT_RELAX = 0x01 << 1,
} SplitFlags;
const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags);
#define FOREACH_WORD(word, length, s, state) \
_FOREACH_WORD(word, length, s, WHITESPACE, 0, state)
#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
_FOREACH_WORD(word, length, s, separator, 0, state)
#define _FOREACH_WORD(word, length, s, separator, flags, state) \
for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags)))
char *strappend(const char *s, const char *suffix);
char *strnappend(const char *s, const char *suffix, size_t length);
char *strjoin_real(const char *x, ...) _sentinel_;
#define strjoin(a, ...) strjoin_real((a), __VA_ARGS__, NULL)
#define strjoina(a, ...) \
({ \
const char *_appendees_[] = { a, __VA_ARGS__ }; \
char *_d_, *_p_; \
size_t _len_ = 0; \
size_t _i_; \
for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
_len_ += strlen(_appendees_[_i_]); \
_p_ = _d_ = alloca(_len_ + 1); \
for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
_p_ = stpcpy(_p_, _appendees_[_i_]); \
*_p_ = 0; \
_d_; \
})
char *strstrip(char *s);
char *delete_chars(char *s, const char *bad);
char *delete_trailing_chars(char *s, const char *bad);
char *truncate_nl(char *s);
static inline char *skip_leading_chars(const char *s, const char *bad) {
if (!s)
return NULL;
if (!bad)
bad = WHITESPACE;
return (char*) s + strspn(s, bad);
}
char ascii_tolower(char x);
char *ascii_strlower(char *s);
char *ascii_strlower_n(char *s, size_t n);
char ascii_toupper(char x);
char *ascii_strupper(char *s);
int ascii_strcasecmp_n(const char *a, const char *b, size_t n);
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m);
bool chars_intersect(const char *a, const char *b) _pure_;
static inline bool _pure_ in_charset(const char *s, const char* charset) {
assert(s);
assert(charset);
return s[strspn(s, charset)] == '\0';
}
bool string_has_cc(const char *p, const char *ok) _pure_;
char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent);
static inline char *ellipsize(const char *s, size_t length, unsigned percent) {
return ellipsize_mem(s, strlen(s), length, percent);
}
char *cellescape(char *buf, size_t len, const char *s);
/* This limit is arbitrary, enough to give some idea what the string contains */
#define CELLESCAPE_DEFAULT_LENGTH 64
bool nulstr_contains(const char *nulstr, const char *needle);
char* strshorten(char *s, size_t l);
char *strreplace(const char *text, const char *old_string, const char *new_string);
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
#define strextend(x, ...) strextend_with_separator(x, NULL, __VA_ARGS__)
char *strrep(const char *s, unsigned n);
int split_pair(const char *s, const char *sep, char **l, char **r);
int free_and_strdup(char **p, const char *s);
int free_and_strndup(char **p, const char *s, size_t l);
/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
if (needlelen <= 0)
return (void*) haystack;
if (haystacklen < needlelen)
return NULL;
assert(haystack);
assert(needle);
return memmem(haystack, haystacklen, needle, needlelen);
}
#if HAVE_EXPLICIT_BZERO
static inline void* explicit_bzero_safe(void *p, size_t l) {
if (l > 0)
explicit_bzero(p, l);
return p;
}
#else
void *explicit_bzero_safe(void *p, size_t l);
#endif
char *string_erase(char *x);
char *string_free_erase(char *s);
DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase);
#define _cleanup_string_free_erase_ _cleanup_(string_free_erasep)
bool string_is_safe(const char *p) _pure_;
static inline size_t strlen_ptr(const char *s) {
if (!s)
return 0;
return strlen(s);
}
/* Like startswith(), but operates on arbitrary memory blocks */
static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
size_t n;
assert(token);
n = strlen(token);
if (sz < n)
return NULL;
assert(p);
if (memcmp(p, token, n) != 0)
return NULL;
return (uint8_t*) p + n;
}
/* Like startswith_no_case(), but operates on arbitrary memory blocks.
* It works only for ASCII strings.
*/
static inline void *memory_startswith_no_case(const void *p, size_t sz, const char *token) {
size_t n, i;
assert(token);
n = strlen(token);
if (sz < n)
return NULL;
assert(p);
for (i = 0; i < n; i++) {
if (ascii_tolower(((char *)p)[i]) != ascii_tolower(token[i]))
return NULL;
}
return (uint8_t*) p + n;
}

View File

@@ -0,0 +1,893 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <fnmatch.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alloc-util.h"
#include "escape.h"
#include "extract-word.h"
#include "fileio.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
char *strv_find(char **l, const char *name) {
char **i;
assert(name);
STRV_FOREACH(i, l)
if (streq(*i, name))
return *i;
return NULL;
}
char *strv_find_prefix(char **l, const char *name) {
char **i;
assert(name);
STRV_FOREACH(i, l)
if (startswith(*i, name))
return *i;
return NULL;
}
char *strv_find_startswith(char **l, const char *name) {
char **i, *e;
assert(name);
/* Like strv_find_prefix, but actually returns only the
* suffix, not the whole item */
STRV_FOREACH(i, l) {
e = startswith(*i, name);
if (e)
return e;
}
return NULL;
}
void strv_clear(char **l) {
char **k;
if (!l)
return;
for (k = l; *k; k++)
free(*k);
*l = NULL;
}
char **strv_free(char **l) {
strv_clear(l);
return mfree(l);
}
char **strv_free_erase(char **l) {
char **i;
STRV_FOREACH(i, l)
string_erase(*i);
return strv_free(l);
}
char **strv_copy(char * const *l) {
char **r, **k;
k = r = new(char*, strv_length(l) + 1);
if (!r)
return NULL;
if (l)
for (; *l; k++, l++) {
*k = strdup(*l);
if (!*k) {
strv_free(r);
return NULL;
}
}
*k = NULL;
return r;
}
size_t strv_length(char * const *l) {
size_t n = 0;
if (!l)
return 0;
for (; *l; l++)
n++;
return n;
}
char **strv_new_ap(const char *x, va_list ap) {
const char *s;
_cleanup_strv_free_ char **a = NULL;
size_t n = 0, i = 0;
va_list aq;
/* As a special trick we ignore all listed strings that equal
* STRV_IGNORE. This is supposed to be used with the
* STRV_IFNOTNULL() macro to include possibly NULL strings in
* the string list. */
if (x) {
n = x == STRV_IGNORE ? 0 : 1;
va_copy(aq, ap);
while ((s = va_arg(aq, const char*))) {
if (s == STRV_IGNORE)
continue;
n++;
}
va_end(aq);
}
a = new(char*, n+1);
if (!a)
return NULL;
if (x) {
if (x != STRV_IGNORE) {
a[i] = strdup(x);
if (!a[i])
return NULL;
i++;
}
while ((s = va_arg(ap, const char*))) {
if (s == STRV_IGNORE)
continue;
a[i] = strdup(s);
if (!a[i])
return NULL;
i++;
}
}
a[i] = NULL;
return TAKE_PTR(a);
}
char **strv_new_internal(const char *x, ...) {
char **r;
va_list ap;
va_start(ap, x);
r = strv_new_ap(x, ap);
va_end(ap);
return r;
}
int strv_extend_strv(char ***a, char **b, bool filter_duplicates) {
char **s, **t;
size_t p, q, i = 0, j;
assert(a);
if (strv_isempty(b))
return 0;
p = strv_length(*a);
q = strv_length(b);
t = reallocarray(*a, p + q + 1, sizeof(char *));
if (!t)
return -ENOMEM;
t[p] = NULL;
*a = t;
STRV_FOREACH(s, b) {
if (filter_duplicates && strv_contains(t, *s))
continue;
t[p+i] = strdup(*s);
if (!t[p+i])
goto rollback;
i++;
t[p+i] = NULL;
}
assert(i <= q);
return (int) i;
rollback:
for (j = 0; j < i; j++)
free(t[p + j]);
t[p] = NULL;
return -ENOMEM;
}
int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
int r;
char **s;
STRV_FOREACH(s, b) {
char *v;
v = strappend(*s, suffix);
if (!v)
return -ENOMEM;
r = strv_push(a, v);
if (r < 0) {
free(v);
return r;
}
}
return 0;
}
char **strv_split_full(const char *s, const char *separator, SplitFlags flags) {
const char *word, *state;
size_t l;
size_t n, i;
char **r;
assert(s);
if (!separator)
separator = WHITESPACE;
s += strspn(s, separator);
if (isempty(s))
return new0(char*, 1);
n = 0;
_FOREACH_WORD(word, l, s, separator, flags, state)
n++;
r = new(char*, n+1);
if (!r)
return NULL;
i = 0;
_FOREACH_WORD(word, l, s, separator, flags, state) {
r[i] = strndup(word, l);
if (!r[i]) {
strv_free(r);
return NULL;
}
i++;
}
r[i] = NULL;
return r;
}
char **strv_split_newlines(const char *s) {
char **l;
size_t n;
assert(s);
/* Special version of strv_split() that splits on newlines and
* suppresses an empty string at the end */
l = strv_split(s, NEWLINE);
if (!l)
return NULL;
n = strv_length(l);
if (n <= 0)
return l;
if (isempty(l[n - 1]))
l[n - 1] = mfree(l[n - 1]);
return l;
}
#if 0 /* NM_IGNORED */
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
_cleanup_strv_free_ char **l = NULL;
size_t n = 0, allocated = 0;
int r;
assert(t);
assert(s);
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&s, &word, separators, flags);
if (r < 0)
return r;
if (r == 0)
break;
if (!GREEDY_REALLOC(l, allocated, n + 2))
return -ENOMEM;
l[n++] = TAKE_PTR(word);
l[n] = NULL;
}
if (!l) {
l = new0(char*, 1);
if (!l)
return -ENOMEM;
}
*t = TAKE_PTR(l);
return (int) n;
}
#endif /* NM_IGNORED */
char *strv_join_prefix(char **l, const char *separator, const char *prefix) {
char *r, *e;
char **s;
size_t n, k, m;
if (!separator)
separator = " ";
k = strlen(separator);
m = strlen_ptr(prefix);
n = 0;
STRV_FOREACH(s, l) {
if (s != l)
n += k;
n += m + strlen(*s);
}
r = new(char, n+1);
if (!r)
return NULL;
e = r;
STRV_FOREACH(s, l) {
if (s != l)
e = stpcpy(e, separator);
if (prefix)
e = stpcpy(e, prefix);
e = stpcpy(e, *s);
}
*e = 0;
return r;
}
int strv_push(char ***l, char *value) {
char **c;
size_t n, m;
if (!value)
return 0;
n = strv_length(*l);
/* Increase and check for overflow */
m = n + 2;
if (m < n)
return -ENOMEM;
c = reallocarray(*l, m, sizeof(char*));
if (!c)
return -ENOMEM;
c[n] = value;
c[n+1] = NULL;
*l = c;
return 0;
}
int strv_push_pair(char ***l, char *a, char *b) {
char **c;
size_t n, m;
if (!a && !b)
return 0;
n = strv_length(*l);
/* increase and check for overflow */
m = n + !!a + !!b + 1;
if (m < n)
return -ENOMEM;
c = reallocarray(*l, m, sizeof(char*));
if (!c)
return -ENOMEM;
if (a)
c[n++] = a;
if (b)
c[n++] = b;
c[n] = NULL;
*l = c;
return 0;
}
int strv_insert(char ***l, size_t position, char *value) {
char **c;
size_t n, m, i;
if (!value)
return 0;
n = strv_length(*l);
position = MIN(position, n);
/* increase and check for overflow */
m = n + 2;
if (m < n)
return -ENOMEM;
c = new(char*, m);
if (!c)
return -ENOMEM;
for (i = 0; i < position; i++)
c[i] = (*l)[i];
c[position] = value;
for (i = position; i < n; i++)
c[i+1] = (*l)[i];
c[n+1] = NULL;
free(*l);
*l = c;
return 0;
}
int strv_consume(char ***l, char *value) {
int r;
r = strv_push(l, value);
if (r < 0)
free(value);
return r;
}
int strv_consume_pair(char ***l, char *a, char *b) {
int r;
r = strv_push_pair(l, a, b);
if (r < 0) {
free(a);
free(b);
}
return r;
}
int strv_consume_prepend(char ***l, char *value) {
int r;
r = strv_push_prepend(l, value);
if (r < 0)
free(value);
return r;
}
int strv_extend(char ***l, const char *value) {
char *v;
if (!value)
return 0;
v = strdup(value);
if (!v)
return -ENOMEM;
return strv_consume(l, v);
}
int strv_extend_front(char ***l, const char *value) {
size_t n, m;
char *v, **c;
assert(l);
/* Like strv_extend(), but prepends rather than appends the new entry */
if (!value)
return 0;
n = strv_length(*l);
/* Increase and overflow check. */
m = n + 2;
if (m < n)
return -ENOMEM;
v = strdup(value);
if (!v)
return -ENOMEM;
c = reallocarray(*l, m, sizeof(char*));
if (!c) {
free(v);
return -ENOMEM;
}
memmove(c+1, c, n * sizeof(char*));
c[0] = v;
c[n+1] = NULL;
*l = c;
return 0;
}
char **strv_uniq(char **l) {
char **i;
/* Drops duplicate entries. The first identical string will be
* kept, the others dropped */
STRV_FOREACH(i, l)
strv_remove(i+1, *i);
return l;
}
bool strv_is_uniq(char **l) {
char **i;
STRV_FOREACH(i, l)
if (strv_find(i+1, *i))
return false;
return true;
}
char **strv_remove(char **l, const char *s) {
char **f, **t;
if (!l)
return NULL;
assert(s);
/* Drops every occurrence of s in the string list, edits
* in-place. */
for (f = t = l; *f; f++)
if (streq(*f, s))
free(*f);
else
*(t++) = *f;
*t = NULL;
return l;
}
char **strv_parse_nulstr(const char *s, size_t l) {
/* l is the length of the input data, which will be split at NULs into
* elements of the resulting strv. Hence, the number of items in the resulting strv
* will be equal to one plus the number of NUL bytes in the l bytes starting at s,
* unless s[l-1] is NUL, in which case the final empty string is not stored in
* the resulting strv, and length is equal to the number of NUL bytes.
*
* Note that contrary to a normal nulstr which cannot contain empty strings, because
* the input data is terminated by any two consequent NUL bytes, this parser accepts
* empty strings in s.
*/
const char *p;
size_t c = 0, i = 0;
char **v;
assert(s || l <= 0);
if (l <= 0)
return new0(char*, 1);
for (p = s; p < s + l; p++)
if (*p == 0)
c++;
if (s[l-1] != 0)
c++;
v = new0(char*, c+1);
if (!v)
return NULL;
p = s;
while (p < s + l) {
const char *e;
e = memchr(p, 0, s + l - p);
v[i] = strndup(p, e ? e - p : s + l - p);
if (!v[i]) {
strv_free(v);
return NULL;
}
i++;
if (!e)
break;
p = e + 1;
}
assert(i == c);
return v;
}
char **strv_split_nulstr(const char *s) {
const char *i;
char **r = NULL;
NULSTR_FOREACH(i, s)
if (strv_extend(&r, i) < 0) {
strv_free(r);
return NULL;
}
if (!r)
return strv_new(NULL);
return r;
}
int strv_make_nulstr(char **l, char **p, size_t *q) {
/* A valid nulstr with two NULs at the end will be created, but
* q will be the length without the two trailing NULs. Thus the output
* string is a valid nulstr and can be iterated over using NULSTR_FOREACH,
* and can also be parsed by strv_parse_nulstr as long as the length
* is provided separately.
*/
size_t n_allocated = 0, n = 0;
_cleanup_free_ char *m = NULL;
char **i;
assert(p);
assert(q);
STRV_FOREACH(i, l) {
size_t z;
z = strlen(*i);
if (!GREEDY_REALLOC(m, n_allocated, n + z + 2))
return -ENOMEM;
memcpy(m + n, *i, z + 1);
n += z + 1;
}
if (!m) {
m = new0(char, 1);
if (!m)
return -ENOMEM;
n = 1;
} else
/* make sure there is a second extra NUL at the end of resulting nulstr */
m[n] = '\0';
assert(n > 0);
*p = m;
*q = n - 1;
m = NULL;
return 0;
}
bool strv_overlap(char **a, char **b) {
char **i;
STRV_FOREACH(i, a)
if (strv_contains(b, *i))
return true;
return false;
}
static int str_compare(char * const *a, char * const *b) {
return strcmp(*a, *b);
}
char **strv_sort(char **l) {
typesafe_qsort(l, strv_length(l), str_compare);
return l;
}
bool strv_equal(char **a, char **b) {
if (strv_isempty(a))
return strv_isempty(b);
if (strv_isempty(b))
return false;
for ( ; *a || *b; ++a, ++b)
if (!streq_ptr(*a, *b))
return false;
return true;
}
void strv_print(char **l) {
char **s;
STRV_FOREACH(s, l)
puts(*s);
}
int strv_extendf(char ***l, const char *format, ...) {
va_list ap;
char *x;
int r;
va_start(ap, format);
r = vasprintf(&x, format, ap);
va_end(ap);
if (r < 0)
return -ENOMEM;
return strv_consume(l, x);
}
char **strv_reverse(char **l) {
size_t n, i;
n = strv_length(l);
if (n <= 1)
return l;
for (i = 0; i < n / 2; i++)
SWAP_TWO(l[i], l[n-1-i]);
return l;
}
char **strv_shell_escape(char **l, const char *bad) {
char **s;
/* Escapes every character in every string in l that is in bad,
* edits in-place, does not roll-back on error. */
STRV_FOREACH(s, l) {
char *v;
v = shell_escape(*s, bad);
if (!v)
return NULL;
free(*s);
*s = v;
}
return l;
}
bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
char* const* p;
STRV_FOREACH(p, patterns)
if (fnmatch(*p, s, flags) == 0)
return true;
return false;
}
char ***strv_free_free(char ***l) {
char ***i;
if (!l)
return NULL;
for (i = l; *i; i++)
strv_free(*i);
return mfree(l);
}
char **strv_skip(char **l, size_t n) {
while (n > 0) {
if (strv_isempty(l))
return l;
l++, n--;
}
return l;
}
int strv_extend_n(char ***l, const char *value, size_t n) {
size_t i, j, k;
char **nl;
assert(l);
if (!value)
return 0;
if (n == 0)
return 0;
/* Adds the value n times to l */
k = strv_length(*l);
nl = reallocarray(*l, k + n + 1, sizeof(char *));
if (!nl)
return -ENOMEM;
*l = nl;
for (i = k; i < k + n; i++) {
nl[i] = strdup(value);
if (!nl[i])
goto rollback;
}
nl[i] = NULL;
return 0;
rollback:
for (j = k; j < i; j++)
free(nl[j]);
nl[k] = NULL;
return -ENOMEM;
}
int fputstrv(FILE *f, char **l, const char *separator, bool *space) {
bool b = false;
char **s;
int r;
/* Like fputs(), but for strv, and with a less stupid argument order */
if (!space)
space = &b;
STRV_FOREACH(s, l) {
r = fputs_with_space(f, *s, separator, space);
if (r < 0)
return r;
}
return 0;
}

View File

@@ -0,0 +1,197 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <fnmatch.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include "alloc-util.h"
#include "extract-word.h"
#include "macro.h"
#include "string-util.h"
#include "util.h"
char *strv_find(char **l, const char *name) _pure_;
char *strv_find_prefix(char **l, const char *name) _pure_;
char *strv_find_startswith(char **l, const char *name) _pure_;
char **strv_free(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
#define _cleanup_strv_free_ _cleanup_(strv_freep)
char **strv_free_erase(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
void strv_clear(char **l);
char **strv_copy(char * const *l);
size_t strv_length(char * const *l) _pure_;
int strv_extend_strv(char ***a, char **b, bool filter_duplicates);
int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
int strv_extend(char ***l, const char *value);
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
int strv_extend_front(char ***l, const char *value);
int strv_push(char ***l, char *value);
int strv_push_pair(char ***l, char *a, char *b);
int strv_insert(char ***l, size_t position, char *value);
static inline int strv_push_prepend(char ***l, char *value) {
return strv_insert(l, 0, value);
}
int strv_consume(char ***l, char *value);
int strv_consume_pair(char ***l, char *a, char *b);
int strv_consume_prepend(char ***l, char *value);
char **strv_remove(char **l, const char *s);
char **strv_uniq(char **l);
bool strv_is_uniq(char **l);
bool strv_equal(char **a, char **b);
#define strv_contains(l, s) (!!strv_find((l), (s)))
char **strv_new_internal(const char *x, ...) _sentinel_;
char **strv_new_ap(const char *x, va_list ap);
#define strv_new(...) strv_new_internal(__VA_ARGS__, NULL)
#define STRV_IGNORE ((const char *) -1)
static inline const char* STRV_IFNOTNULL(const char *x) {
return x ? x : STRV_IGNORE;
}
static inline bool strv_isempty(char * const *l) {
return !l || !*l;
}
char **strv_split_full(const char *s, const char *separator, SplitFlags flags);
static inline char **strv_split(const char *s, const char *separator) {
return strv_split_full(s, separator, 0);
}
char **strv_split_newlines(const char *s);
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
char *strv_join_prefix(char **l, const char *separator, const char *prefix);
static inline char *strv_join(char **l, const char *separator) {
return strv_join_prefix(l, separator, NULL);
}
char **strv_parse_nulstr(const char *s, size_t l);
char **strv_split_nulstr(const char *s);
int strv_make_nulstr(char **l, char **p, size_t *n);
bool strv_overlap(char **a, char **b) _pure_;
#define STRV_FOREACH(s, l) \
for ((s) = (l); (s) && *(s); (s)++)
#define STRV_FOREACH_BACKWARDS(s, l) \
for (s = ({ \
char **_l = l; \
_l ? _l + strv_length(_l) - 1U : NULL; \
}); \
(l) && ((s) >= (l)); \
(s)--)
#define STRV_FOREACH_PAIR(x, y, l) \
for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
char **strv_sort(char **l);
void strv_print(char **l);
#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
#define strv_from_stdarg_alloca(first) \
({ \
char **_l; \
\
if (!first) \
_l = (char**) &first; \
else { \
size_t _n; \
va_list _ap; \
\
_n = 1; \
va_start(_ap, first); \
while (va_arg(_ap, char*)) \
_n++; \
va_end(_ap); \
\
_l = newa(char*, _n+1); \
_l[_n = 0] = (char*) first; \
va_start(_ap, first); \
for (;;) { \
_l[++_n] = va_arg(_ap, char*); \
if (!_l[_n]) \
break; \
} \
va_end(_ap); \
} \
_l; \
})
#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x)
#define STRPTR_IN_SET(x, ...) \
({ \
const char* _x = (x); \
_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__); \
x = _ll ? _ll[0] : NULL; \
_ll; \
}); \
_l && *_l; \
x = ({ \
_l ++; \
_l[0]; \
}))
char **strv_reverse(char **l);
char **strv_shell_escape(char **l, const char *bad);
bool strv_fnmatch(char* const* patterns, const char *s, int flags);
static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) {
assert(s);
return strv_isempty(patterns) ||
strv_fnmatch(patterns, s, flags);
}
char ***strv_free_free(char ***l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free);
char **strv_skip(char **l, size_t n);
int strv_extend_n(char ***l, const char *value, size_t n);
int fputstrv(FILE *f, char **l, const char *separator, bool *space);
#define strv_free_and_replace(a, b) \
({ \
strv_free(a); \
(a) = (b); \
(b) = NULL; \
0; \
})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
typedef uint64_t usec_t;
typedef uint64_t nsec_t;
#define PRI_NSEC PRIu64
#define PRI_USEC PRIu64
#define NSEC_FMT "%" PRI_NSEC
#define USEC_FMT "%" PRI_USEC
#include "macro.h"
typedef struct dual_timestamp {
usec_t realtime;
usec_t monotonic;
} dual_timestamp;
typedef struct triple_timestamp {
usec_t realtime;
usec_t monotonic;
usec_t boottime;
} triple_timestamp;
#define USEC_INFINITY ((usec_t) -1)
#define NSEC_INFINITY ((nsec_t) -1)
#define MSEC_PER_SEC 1000ULL
#define USEC_PER_SEC ((usec_t) 1000000ULL)
#define USEC_PER_MSEC ((usec_t) 1000ULL)
#define NSEC_PER_SEC ((nsec_t) 1000000000ULL)
#define NSEC_PER_MSEC ((nsec_t) 1000000ULL)
#define NSEC_PER_USEC ((nsec_t) 1000ULL)
#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC))
#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC))
#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE))
#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE))
#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR))
#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR))
#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY))
#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY))
#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC))
#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC))
#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC))
#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC))
/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this
* to 6. Let's rely on that. */
#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1)
#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */
#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
#define FORMAT_TIMESPAN_MAX 64
#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1)
#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {})
#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {})
usec_t now(clockid_t clock);
nsec_t now_nsec(clockid_t clock);
dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u);
triple_timestamp* triple_timestamp_get(triple_timestamp *ts);
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u);
#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC)
#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM)
static inline bool dual_timestamp_is_set(const dual_timestamp *ts) {
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
}
static inline bool triple_timestamp_is_set(const triple_timestamp *ts) {
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) ||
(ts->boottime > 0 && ts->boottime != USEC_INFINITY));
}
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
usec_t timespec_load(const struct timespec *ts) _pure_;
nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
struct timespec *timespec_store(struct timespec *ts, usec_t u);
usec_t timeval_load(const struct timeval *tv) _pure_;
struct timeval *timeval_store(struct timeval *tv, usec_t u);
char *format_timestamp(char *buf, size_t l, usec_t t);
char *format_timestamp_utc(char *buf, size_t l, usec_t t);
char *format_timestamp_us(char *buf, size_t l, usec_t t);
char *format_timestamp_us_utc(char *buf, size_t l, usec_t t);
char *format_timestamp_relative(char *buf, size_t l, usec_t t);
char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
int parse_timestamp(const char *t, usec_t *usec);
int parse_sec(const char *t, usec_t *usec);
int parse_sec_fix_0(const char *t, usec_t *usec);
int parse_time(const char *t, usec_t *usec, usec_t default_unit);
int parse_nsec(const char *t, nsec_t *nsec);
bool ntp_synced(void);
int get_timezones(char ***l);
bool timezone_is_valid(const char *name, int log_level);
bool clock_boottime_supported(void);
bool clock_supported(clockid_t clock);
clockid_t clock_boottime_or_monotonic(void);
usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to);
int get_timezone(char **timezone);
time_t mktime_or_timegm(struct tm *tm, bool utc);
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
unsigned long usec_to_jiffies(usec_t usec);
bool in_utc_timezone(void);
static inline usec_t usec_add(usec_t a, usec_t b) {
usec_t c;
/* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't
* overflow. */
c = a + b;
if (c < a || c < b) /* overflow check */
return USEC_INFINITY;
return c;
}
static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) {
if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */
return USEC_INFINITY;
if (timestamp < delta)
return 0;
return timestamp - delta;
}
static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
if (delta < 0)
return usec_add(timestamp, (usec_t) (-delta));
else
return usec_sub_unsigned(timestamp, (usec_t) delta);
}
#if SIZEOF_TIME_T == 8
/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year
* territory. However, since we want to stay away from this in all timezones we take one day off. */
#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
#elif SIZEOF_TIME_T == 4
/* With a 32bit time_t we can't go beyond 2038... */
#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
#else
#error "Yuck, time_t is neither 4 nor 8 bytes wide?"
#endif
int time_change_fd(void);

View File

@@ -0,0 +1,335 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.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 */

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

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "macro.h"
static inline void umaskp(mode_t *u) {
umask(*u);
}
#define _cleanup_umask_ _cleanup_(umaskp)
struct _umask_struct_ {
mode_t mask;
bool quit;
};
static inline void _reset_umask_(struct _umask_struct_ *s) {
umask(s->mask);
};
#define RUN_WITH_UMASK(mask) \
for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \
!_saved_umask_.quit ; \
_saved_umask_.quit = true)

View File

@@ -0,0 +1,546 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
/* Parts of this file are based on the GLIB utf8 validation functions. The
* original license text follows. */
/* gutf8.c - Operations on UTF-8 strings.
*
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "alloc-util.h"
#include "gunicode.h"
#include "hexdecoct.h"
#include "macro.h"
#include "utf8.h"
bool unichar_is_valid(char32_t ch) {
if (ch >= 0x110000) /* End of unicode space */
return false;
if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
return false;
if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
return false;
if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
return false;
return true;
}
#if 0 /* NM_IGNORED */
static bool unichar_is_control(char32_t ch) {
/*
0 to ' '-1 is the C0 range.
DEL=0x7F, and DEL+1 to 0x9F is C1 range.
'\t' is in C0 range, but more or less harmless and commonly used.
*/
return (ch < ' ' && !IN_SET(ch, '\t', '\n')) ||
(0x7F <= ch && ch <= 0x9F);
}
#endif /* NM_IGNORED */
/* count of characters used to encode one unicode char */
static size_t utf8_encoded_expected_len(const char *str) {
uint8_t c;
assert(str);
c = (uint8_t) str[0];
if (c < 0x80)
return 1;
if ((c & 0xe0) == 0xc0)
return 2;
if ((c & 0xf0) == 0xe0)
return 3;
if ((c & 0xf8) == 0xf0)
return 4;
if ((c & 0xfc) == 0xf8)
return 5;
if ((c & 0xfe) == 0xfc)
return 6;
return 0;
}
/* decode one unicode char */
int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
char32_t unichar;
size_t len, i;
assert(str);
len = utf8_encoded_expected_len(str);
switch (len) {
case 1:
*ret_unichar = (char32_t)str[0];
return 0;
case 2:
unichar = str[0] & 0x1f;
break;
case 3:
unichar = (char32_t)str[0] & 0x0f;
break;
case 4:
unichar = (char32_t)str[0] & 0x07;
break;
case 5:
unichar = (char32_t)str[0] & 0x03;
break;
case 6:
unichar = (char32_t)str[0] & 0x01;
break;
default:
return -EINVAL;
}
for (i = 1; i < len; i++) {
if (((char32_t)str[i] & 0xc0) != 0x80)
return -EINVAL;
unichar <<= 6;
unichar |= (char32_t)str[i] & 0x3f;
}
*ret_unichar = unichar;
return 0;
}
#if 0 /* NM_IGNORED */
bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
const char *p;
assert(str);
for (p = str; length;) {
int encoded_len, r;
char32_t val;
encoded_len = utf8_encoded_valid_unichar(p);
if (encoded_len < 0 ||
(size_t) encoded_len > length)
return false;
r = utf8_encoded_to_unichar(p, &val);
if (r < 0 ||
unichar_is_control(val) ||
(!newline && val == '\n'))
return false;
length -= encoded_len;
p += encoded_len;
}
return true;
}
#endif /* NM_IGNORED */
char *utf8_is_valid(const char *str) {
const char *p;
assert(str);
p = str;
while (*p) {
int len;
len = utf8_encoded_valid_unichar(p);
if (len < 0)
return NULL;
p += len;
}
return (char*) str;
}
char *utf8_escape_invalid(const char *str) {
char *p, *s;
assert(str);
p = s = malloc(strlen(str) * 4 + 1);
if (!p)
return NULL;
while (*str) {
int len;
len = utf8_encoded_valid_unichar(str);
if (len > 0) {
s = mempcpy(s, str, len);
str += len;
} else {
s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
str += 1;
}
}
*s = '\0';
return p;
}
#if 0 /* NM_IGNORED */
char *utf8_escape_non_printable(const char *str) {
char *p, *s;
assert(str);
p = s = malloc(strlen(str) * 4 + 1);
if (!p)
return NULL;
while (*str) {
int len;
len = utf8_encoded_valid_unichar(str);
if (len > 0) {
if (utf8_is_printable(str, len)) {
s = mempcpy(s, str, len);
str += len;
} else {
while (len > 0) {
*(s++) = '\\';
*(s++) = 'x';
*(s++) = hexchar((int) *str >> 4);
*(s++) = hexchar((int) *str);
str += 1;
len--;
}
}
} else {
s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
str += 1;
}
}
*s = '\0';
return p;
}
#endif /* NM_IGNORED */
char *ascii_is_valid(const char *str) {
const char *p;
/* Check whether the string consists of valid ASCII bytes,
* i.e values between 0 and 127, inclusive. */
assert(str);
for (p = str; *p; p++)
if ((unsigned char) *p >= 128)
return NULL;
return (char*) str;
}
#if 0 /* NM_IGNORED */
char *ascii_is_valid_n(const char *str, size_t len) {
size_t i;
/* Very similar to ascii_is_valid(), but checks exactly len
* bytes and rejects any NULs in that range. */
assert(str);
for (i = 0; i < len; i++)
if ((unsigned char) str[i] >= 128 || str[i] == 0)
return NULL;
return (char*) str;
}
#endif /* NM_IGNORED */
/**
* utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
* @out_utf8: output buffer of at least 4 bytes or NULL
* @g: UCS-4 character to encode
*
* This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
* The length of the character is returned. It is not zero-terminated! If the
* output buffer is NULL, only the length is returned.
*
* Returns: The length in bytes that the UTF-8 representation does or would
* occupy.
*/
size_t utf8_encode_unichar(char *out_utf8, char32_t g) {
if (g < (1 << 7)) {
if (out_utf8)
out_utf8[0] = g & 0x7f;
return 1;
} else if (g < (1 << 11)) {
if (out_utf8) {
out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
out_utf8[1] = 0x80 | (g & 0x3f);
}
return 2;
} else if (g < (1 << 16)) {
if (out_utf8) {
out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
out_utf8[2] = 0x80 | (g & 0x3f);
}
return 3;
} else if (g < (1 << 21)) {
if (out_utf8) {
out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
out_utf8[3] = 0x80 | (g & 0x3f);
}
return 4;
}
return 0;
}
#if 0 /* NM_IGNORED */
char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
const uint8_t *f;
char *r, *t;
assert(s);
/* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may
* take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */
if (length * 2 < length)
return NULL; /* overflow */
r = new(char, length * 2 + 1);
if (!r)
return NULL;
f = (const uint8_t*) s;
t = r;
while (f + 1 < (const uint8_t*) s + length) {
char16_t w1, w2;
/* see RFC 2781 section 2.2 */
w1 = f[1] << 8 | f[0];
f += 2;
if (!utf16_is_surrogate(w1)) {
t += utf8_encode_unichar(t, w1);
continue;
}
if (utf16_is_trailing_surrogate(w1))
continue; /* spurious trailing surrogate, ignore */
if (f + 1 >= (const uint8_t*) s + length)
break;
w2 = f[1] << 8 | f[0];
f += 2;
if (!utf16_is_trailing_surrogate(w2)) {
f -= 2;
continue; /* surrogate missing its trailing surrogate, ignore */
}
t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
}
*t = 0;
return r;
}
size_t utf16_encode_unichar(char16_t *out, char32_t c) {
/* Note that this encodes as little-endian. */
switch (c) {
case 0 ... 0xd7ffU:
case 0xe000U ... 0xffffU:
out[0] = htole16(c);
return 1;
case 0x10000U ... 0x10ffffU:
c -= 0x10000U;
out[0] = htole16((c >> 10) + 0xd800U);
out[1] = htole16((c & 0x3ffU) + 0xdc00U);
return 2;
default: /* A surrogate (invalid) */
return 0;
}
}
char16_t *utf8_to_utf16(const char *s, size_t length) {
char16_t *n, *p;
size_t i;
int r;
assert(s);
n = new(char16_t, length + 1);
if (!n)
return NULL;
p = n;
for (i = 0; i < length;) {
char32_t unichar;
size_t e;
e = utf8_encoded_expected_len(s + i);
if (e <= 1) /* Invalid and single byte characters are copied as they are */
goto copy;
if (i + e > length) /* sequence longer than input buffer, then copy as-is */
goto copy;
r = utf8_encoded_to_unichar(s + i, &unichar);
if (r < 0) /* sequence invalid, then copy as-is */
goto copy;
p += utf16_encode_unichar(p, unichar);
i += e;
continue;
copy:
*(p++) = htole16(s[i++]);
}
*p = 0;
return n;
}
size_t char16_strlen(const char16_t *s) {
size_t n = 0;
assert(s);
while (*s != 0)
n++, s++;
return n;
}
#endif /* NM_IGNORED */
/* expected size used to encode one unicode char */
static int utf8_unichar_to_encoded_len(char32_t unichar) {
if (unichar < 0x80)
return 1;
if (unichar < 0x800)
return 2;
if (unichar < 0x10000)
return 3;
if (unichar < 0x200000)
return 4;
if (unichar < 0x4000000)
return 5;
return 6;
}
/* validate one encoded unicode char and return its length */
int utf8_encoded_valid_unichar(const char *str) {
char32_t unichar;
size_t len, i;
int r;
assert(str);
len = utf8_encoded_expected_len(str);
if (len == 0)
return -EINVAL;
/* ascii is valid */
if (len == 1)
return 1;
/* check if expected encoded chars are available */
for (i = 0; i < len; i++)
if ((str[i] & 0x80) != 0x80)
return -EINVAL;
r = utf8_encoded_to_unichar(str, &unichar);
if (r < 0)
return r;
/* check if encoded length matches encoded value */
if (utf8_unichar_to_encoded_len(unichar) != (int) len)
return -EINVAL;
/* check if value has valid range */
if (!unichar_is_valid(unichar))
return -EINVAL;
return (int) len;
}
#if 0 /* NM_IGNORED */
size_t utf8_n_codepoints(const char *str) {
size_t n = 0;
/* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */
while (*str != 0) {
int k;
k = utf8_encoded_valid_unichar(str);
if (k < 0)
return (size_t) -1;
str += k;
n++;
}
return n;
}
size_t utf8_console_width(const char *str) {
size_t n = 0;
/* Returns the approximate width a string will take on screen when printed on a character cell
* terminal/console. */
while (*str != 0) {
char32_t c;
if (utf8_encoded_to_unichar(str, &c) < 0)
return (size_t) -1;
str = utf8_next_char(str);
n += unichar_iswide(c) ? 2 : 1;
}
return n;
}
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <uchar.h>
#include "macro.h"
#include "missing_type.h"
#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf"
bool unichar_is_valid(char32_t c);
char *utf8_is_valid(const char *s) _pure_;
char *ascii_is_valid(const char *s) _pure_;
char *ascii_is_valid_n(const char *str, size_t len);
bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_;
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
char *utf8_escape_invalid(const char *s);
char *utf8_escape_non_printable(const char *str);
size_t utf8_encode_unichar(char *out_utf8, char32_t g);
size_t utf16_encode_unichar(char16_t *out, char32_t c);
char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */);
char16_t *utf8_to_utf16(const char *s, size_t length);
size_t char16_strlen(const char16_t *s); /* returns the number of 16bit words in the string (not bytes!) */
int utf8_encoded_valid_unichar(const char *str);
int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar);
static inline bool utf16_is_surrogate(char16_t c) {
return c >= 0xd800U && c <= 0xdfffU;
}
static inline bool utf16_is_trailing_surrogate(char16_t c) {
return c >= 0xdc00U && c <= 0xdfffU;
}
static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) {
return ((((char32_t) lead - 0xd800U) << 10) + ((char32_t) trail - 0xdc00U) + 0x10000U);
}
size_t utf8_n_codepoints(const char *str);
size_t utf8_console_width(const char *str);

View File

@@ -0,0 +1,643 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt-shared.h"
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/statfs.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
#include "alloc-util.h"
#include "btrfs-util.h"
#include "build.h"
#include "cgroup-util.h"
#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"
#include "format-util.h"
#include "hashmap.h"
#include "hostname-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "procfs-util.h"
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
#include "virt.h"
#if 0 /* NM_IGNORED */
int saved_argc = 0;
char **saved_argv = NULL;
static int saved_in_initrd = -1;
#endif /* NM_IGNORED */
size_t page_size(void) {
static thread_local size_t pgsz = 0;
long r;
if (_likely_(pgsz > 0))
return pgsz;
r = sysconf(_SC_PAGESIZE);
assert(r > 0);
pgsz = (size_t) r;
return pgsz;
}
#if 0 /* NM_IGNORED */
bool plymouth_running(void) {
return access("/run/plymouth/pid", F_OK) >= 0;
}
bool display_is_local(const char *display) {
assert(display);
return
display[0] == ':' &&
display[1] >= '0' &&
display[1] <= '9';
}
bool kexec_loaded(void) {
_cleanup_free_ char *s = NULL;
if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0)
return false;
return s[0] == '1';
}
int prot_from_flags(int flags) {
switch (flags & O_ACCMODE) {
case O_RDONLY:
return PROT_READ;
case O_WRONLY:
return PROT_WRITE;
case O_RDWR:
return PROT_READ|PROT_WRITE;
default:
return -EINVAL;
}
}
bool in_initrd(void) {
struct statfs s;
int r;
if (saved_in_initrd >= 0)
return saved_in_initrd;
/* We make two checks here:
*
* 1. the flag file /etc/initrd-release must exist
* 2. the root file system must be a memory file system
*
* The second check is extra paranoia, since misdetecting an
* initrd can have bad consequences due the initrd
* emptying when transititioning to the main systemd.
*/
r = getenv_bool_secure("SYSTEMD_IN_INITRD");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m");
if (r >= 0)
saved_in_initrd = r > 0;
else
saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
statfs("/", &s) >= 0 &&
is_temporary_fs(&s);
return saved_in_initrd;
}
void in_initrd_force(bool value) {
saved_in_initrd = value;
}
/* hey glibc, APIs with callbacks without a user pointer are so useless */
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
__compar_d_fn_t compar, void *arg) {
size_t l, u, idx;
const void *p;
int comparison;
assert(!size_multiply_overflow(nmemb, size));
l = 0;
u = nmemb;
while (l < u) {
idx = (l + u) / 2;
p = (const uint8_t*) base + idx * size;
comparison = compar(key, p, arg);
if (comparison < 0)
u = idx;
else if (comparison > 0)
l = idx + 1;
else
return (void *)p;
}
return NULL;
}
bool memeqzero(const void *data, size_t length) {
/* Does the buffer consist entirely of NULs?
* Copied from https://github.com/systemd/casync/, copied in turn from
* https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92,
* which is licensed CC-0.
*/
const uint8_t *p = data;
size_t i;
/* Check first 16 bytes manually */
for (i = 0; i < 16; i++, length--) {
if (length == 0)
return true;
if (p[i])
return false;
}
/* Now we know first 16 bytes are NUL, memcmp with self. */
return memcmp(data, p + i, length) == 0;
}
int on_ac_power(void) {
bool found_offline = false, found_online = false;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
d = opendir("/sys/class/power_supply");
if (!d)
return errno == ENOENT ? true : -errno;
FOREACH_DIRENT(de, d, return -errno) {
_cleanup_close_ int fd = -1, device = -1;
char contents[6];
ssize_t n;
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (device < 0) {
if (IN_SET(errno, ENOENT, ENOTDIR))
continue;
return -errno;
}
fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
n = read(fd, contents, sizeof(contents));
if (n < 0)
return -errno;
if (n != 6 || memcmp(contents, "Mains\n", 6))
continue;
safe_close(fd);
fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
n = read(fd, contents, sizeof(contents));
if (n < 0)
return -errno;
if (n != 2 || contents[1] != '\n')
return -EIO;
if (contents[0] == '1') {
found_online = true;
break;
} else if (contents[0] == '0')
found_offline = true;
else
return -EIO;
}
return found_online || !found_offline;
}
int container_get_leader(const char *machine, pid_t *pid) {
_cleanup_free_ char *s = NULL, *class = NULL;
const char *p;
pid_t leader;
int r;
assert(machine);
assert(pid);
if (streq(machine, ".host")) {
*pid = 1;
return 0;
}
if (!machine_name_is_valid(machine))
return -EINVAL;
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(NULL, p,
"LEADER", &s,
"CLASS", &class);
if (r == -ENOENT)
return -EHOSTDOWN;
if (r < 0)
return r;
if (!s)
return -EIO;
if (!streq_ptr(class, "container"))
return -EIO;
r = parse_pid(s, &leader);
if (r < 0)
return r;
if (leader <= 1)
return -EIO;
*pid = leader;
return 0;
}
int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
int rfd = -1;
assert(pid >= 0);
if (mntns_fd) {
const char *mntns;
mntns = procfs_file_alloca(pid, "ns/mnt");
mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (mntnsfd < 0)
return -errno;
}
if (pidns_fd) {
const char *pidns;
pidns = procfs_file_alloca(pid, "ns/pid");
pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (pidnsfd < 0)
return -errno;
}
if (netns_fd) {
const char *netns;
netns = procfs_file_alloca(pid, "ns/net");
netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (netnsfd < 0)
return -errno;
}
if (userns_fd) {
const char *userns;
userns = procfs_file_alloca(pid, "ns/user");
usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (usernsfd < 0 && errno != ENOENT)
return -errno;
}
if (root_fd) {
const char *root;
root = procfs_file_alloca(pid, "root");
rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (rfd < 0)
return -errno;
}
if (pidns_fd)
*pidns_fd = pidnsfd;
if (mntns_fd)
*mntns_fd = mntnsfd;
if (netns_fd)
*netns_fd = netnsfd;
if (userns_fd)
*userns_fd = usernsfd;
if (root_fd)
*root_fd = rfd;
pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
return 0;
}
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
if (userns_fd >= 0) {
/* Can't setns to your own userns, since then you could
* escalate from non-root to root in your own namespace, so
* check if namespaces equal before attempting to enter. */
_cleanup_free_ char *userns_fd_path = NULL;
int r;
if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
return -ENOMEM;
r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
if (r < 0)
return r;
if (r)
userns_fd = -1;
}
if (pidns_fd >= 0)
if (setns(pidns_fd, CLONE_NEWPID) < 0)
return -errno;
if (mntns_fd >= 0)
if (setns(mntns_fd, CLONE_NEWNS) < 0)
return -errno;
if (netns_fd >= 0)
if (setns(netns_fd, CLONE_NEWNET) < 0)
return -errno;
if (userns_fd >= 0)
if (setns(userns_fd, CLONE_NEWUSER) < 0)
return -errno;
if (root_fd >= 0) {
if (fchdir(root_fd) < 0)
return -errno;
if (chroot(".") < 0)
return -errno;
}
return reset_uid_gid();
}
uint64_t physical_memory(void) {
_cleanup_free_ char *root = NULL, *value = NULL;
uint64_t mem, lim;
size_t ps;
long sc;
int r;
/* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
* memory.
*
* In order to support containers nicely that have a configured memory limit we'll take the minimum of the
* physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
sc = sysconf(_SC_PHYS_PAGES);
assert(sc > 0);
ps = page_size();
mem = (uint64_t) sc * (uint64_t) ps;
r = cg_get_root_path(&root);
if (r < 0) {
log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m");
return mem;
}
r = cg_all_unified();
if (r < 0) {
log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m");
return mem;
}
if (r > 0) {
r = cg_get_attribute("memory", root, "memory.max", &value);
if (r < 0) {
log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m");
return mem;
}
if (streq(value, "max"))
return mem;
} else {
r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value);
if (r < 0) {
log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m");
return mem;
}
}
r = safe_atou64(value, &lim);
if (r < 0) {
log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value);
return mem;
}
if (lim == UINT64_MAX)
return mem;
/* Make sure the limit is a multiple of our own page size */
lim /= ps;
lim *= ps;
return MIN(mem, lim);
}
uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
uint64_t p, m, ps, r;
assert(max > 0);
/* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
* the result is a multiple of the page size (rounds down). */
ps = page_size();
assert(ps > 0);
p = physical_memory() / ps;
assert(p > 0);
m = p * v;
if (m / p != v)
return UINT64_MAX;
m /= max;
r = m * ps;
if (r / ps != m)
return UINT64_MAX;
return r;
}
uint64_t system_tasks_max(void) {
uint64_t a = TASKS_MAX, b = TASKS_MAX;
_cleanup_free_ char *root = NULL;
int r;
/* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
* limit:
*
* a) the maximum tasks value the kernel allows on this architecture
* b) the cgroups pids_max attribute for the system
* c) the kernel's configured maximum PID value
*
* And then pick the smallest of the three */
r = procfs_tasks_get_limit(&a);
if (r < 0)
log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m");
r = cg_get_root_path(&root);
if (r < 0)
log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m");
else {
_cleanup_free_ char *value = NULL;
r = cg_get_attribute("pids", root, "pids.max", &value);
if (r < 0)
log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m");
else if (!streq(value, "max")) {
r = safe_atou64(value, &b);
if (r < 0)
log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m");
}
}
return MIN3(TASKS_MAX,
a <= 0 ? TASKS_MAX : a,
b <= 0 ? TASKS_MAX : b);
}
uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
uint64_t t, m;
assert(max > 0);
/* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
* relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
t = system_tasks_max();
assert(t > 0);
m = t * v;
if (m / t != v) /* overflow? */
return UINT64_MAX;
return m / max;
}
int version(void) {
puts(PACKAGE_STRING "\n"
SYSTEMD_FEATURES);
return 0;
}
/* This is a direct translation of str_verscmp from boot.c */
static bool is_digit(int c) {
return c >= '0' && c <= '9';
}
static int c_order(int c) {
if (c == 0 || is_digit(c))
return 0;
if ((c >= 'a') && (c <= 'z'))
return c;
return c + 0x10000;
}
int str_verscmp(const char *s1, const char *s2) {
const char *os1, *os2;
assert(s1);
assert(s2);
os1 = s1;
os2 = s2;
while (*s1 || *s2) {
int first;
while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
int order;
order = c_order(*s1) - c_order(*s2);
if (order != 0)
return order;
s1++;
s2++;
}
while (*s1 == '0')
s1++;
while (*s2 == '0')
s2++;
first = 0;
while (is_digit(*s1) && is_digit(*s2)) {
if (first == 0)
first = *s1 - *s2;
s1++;
s2++;
}
if (is_digit(*s1))
return 1;
if (is_digit(*s2))
return -1;
if (first != 0)
return first;
}
return strcmp(os1, os2);
}
/* Turn off core dumps but only if we're running outside of a container. */
void disable_coredumps(void) {
int r;
if (detect_container() > 0)
return;
r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
}
#endif /* NM_IGNORED */

View File

@@ -0,0 +1,244 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <locale.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "format-util.h"
#include "macro.h"
#include "time-util.h"
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
static inline const char* yes_no(bool b) {
return b ? "yes" : "no";
}
static inline const char* true_false(bool b) {
return b ? "true" : "false";
}
static inline const char* one_zero(bool b) {
return b ? "1" : "0";
}
static inline const char* enable_disable(bool b) {
return b ? "enable" : "disable";
}
bool plymouth_running(void);
bool display_is_local(const char *display) _pure_;
#define NULSTR_FOREACH(i, l) \
for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
#define NULSTR_FOREACH_PAIR(i, j, l) \
for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
extern int saved_argc;
extern char **saved_argv;
bool kexec_loaded(void);
int prot_from_flags(int flags) _const_;
bool in_initrd(void);
void in_initrd_force(bool value);
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
__compar_d_fn_t compar, void *arg);
#define typesafe_bsearch_r(k, b, n, func, userdata) \
({ \
const typeof(b[0]) *_k = k; \
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \
})
/**
* Normal bsearch requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
static inline void* bsearch_safe(const void *key, const void *base,
size_t nmemb, size_t size, __compar_fn_t compar) {
if (nmemb <= 0)
return NULL;
assert(base);
return bsearch(key, base, nmemb, size, compar);
}
#define typesafe_bsearch(k, b, n, func) \
({ \
const typeof(b[0]) *_k = k; \
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \
})
/**
* Normal qsort requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
if (nmemb <= 1)
return;
assert(base);
qsort(base, nmemb, size, compar);
}
/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so
* is the prototype for the comparison function */
#define typesafe_qsort(p, n, func) \
({ \
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
})
static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
if (nmemb <= 1)
return;
assert(base);
qsort_r(base, nmemb, size, compar, userdata);
}
#define typesafe_qsort_r(p, n, func, userdata) \
({ \
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \
})
/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
static inline void memcpy_safe(void *dst, const void *src, size_t n) {
if (n == 0)
return;
assert(src);
memcpy(dst, src, n);
}
/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */
static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
if (n == 0)
return 0;
assert(s1);
assert(s2);
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) \
({ \
size_t _l_ = (l); \
void *_x_ = (x); \
_l_ == 0 ? _x_ : memset(_x_, 0, _l_); \
})
#define zero(x) (memzero(&(x), sizeof(x)))
bool memeqzero(const void *data, size_t length);
#define eqzero(x) memeqzero(x, sizeof(x))
static inline void *mempset(void *s, int c, size_t n) {
memset(s, c, n);
return (uint8_t*)s + n;
}
static inline void _reset_errno_(int *saved_errno) {
errno = *saved_errno;
}
#define PROTECT_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
* negative. Instead of "return -errno;", use "return negative_errno();"
* It will suppress bogus gcc warnings in case it assumes 'errno' might
* be 0 and thus the caller's error-handling might not be triggered. */
assert_return(errno > 0, -EINVAL);
return -errno;
}
static inline unsigned u64log2(uint64_t n) {
#if __SIZEOF_LONG_LONG__ == 8
return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0;
#else
#error "Wut?"
#endif
}
static inline unsigned u32ctz(uint32_t n) {
#if __SIZEOF_INT__ == 4
return n != 0 ? __builtin_ctz(n) : 32;
#else
#error "Wut?"
#endif
}
static inline unsigned log2i(int x) {
assert(x > 0);
return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1;
}
static inline unsigned log2u(unsigned x) {
assert(x > 0);
return sizeof(unsigned) * 8 - __builtin_clz(x) - 1;
}
static inline unsigned log2u_round_up(unsigned x) {
assert(x > 0);
if (x == 1)
return 0;
return log2u(x - 1) + 1;
}
int container_get_leader(const char *machine, pid_t *pid);
int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
uint64_t physical_memory(void);
uint64_t physical_memory_scale(uint64_t v, uint64_t max);
uint64_t system_tasks_max(void);
uint64_t system_tasks_max_scale(uint64_t v, uint64_t max);
int version(void);
int str_verscmp(const char *s1, const char *s2);
void disable_coredumps(void);