diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2015-11-23 11:23:25 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2015-11-23 11:23:25 +0100 |
commit | aa9d0d1467ae3569b7c3161cabda2cbe62056526 (patch) | |
tree | 14a9408d307e55f28159564f4f751afca0c94aa9 | |
parent | 8bfe99a1aeaabc552838f6d86770d54f9271ca1b (diff) | |
download | NetworkManager-aa9d0d1467ae3569b7c3161cabda2cbe62056526.tar.gz |
systemd: update code from upstream
This is a direct dump from systemd git on 2015-11-23, git commit
6098bb0dc31b1c472ab868088d04234352c44b75.
======
SYSTEMD_DIR=../systemd
COMMIT=6098bb0dc31b1c472ab868088d04234352c44b75
(
cd "$SYSTEMD_DIR"
git checkout "$COMMIT"
git reset --hard
git clean -fdx
)
git ls-files :/src/systemd/src/ | xargs -d '\n' rm -f
nm_copy_sd() {
mkdir -p "./src/systemd/$(dirname "$1")"
cp "$SYSTEMD_DIR/$1" "./src/systemd/$1"
}
nm_copy_sd "src/basic/alloc-util.c"
nm_copy_sd "src/basic/alloc-util.h"
nm_copy_sd "src/basic/async.h"
nm_copy_sd "src/basic/escape.c"
nm_copy_sd "src/basic/escape.h"
nm_copy_sd "src/basic/fileio.c"
nm_copy_sd "src/basic/fileio.h"
nm_copy_sd "src/basic/fd-util.c"
nm_copy_sd "src/basic/fd-util.h"
nm_copy_sd "src/basic/fs-util.c"
nm_copy_sd "src/basic/fs-util.h"
nm_copy_sd "src/basic/hashmap.c"
nm_copy_sd "src/basic/hashmap.h"
nm_copy_sd "src/basic/hexdecoct.c"
nm_copy_sd "src/basic/hexdecoct.h"
nm_copy_sd "src/basic/hostname-util.c"
nm_copy_sd "src/basic/hostname-util.h"
nm_copy_sd "src/basic/in-addr-util.c"
nm_copy_sd "src/basic/in-addr-util.h"
nm_copy_sd "src/basic/io-util.c"
nm_copy_sd "src/basic/io-util.h"
nm_copy_sd "src/basic/list.h"
nm_copy_sd "src/basic/log.h"
nm_copy_sd "src/basic/macro.h"
nm_copy_sd "src/basic/mempool.h"
nm_copy_sd "src/basic/mempool.c"
nm_copy_sd "src/basic/parse-util.c"
nm_copy_sd "src/basic/parse-util.h"
nm_copy_sd "src/basic/path-util.c"
nm_copy_sd "src/basic/path-util.h"
nm_copy_sd "src/basic/prioq.h"
nm_copy_sd "src/basic/prioq.c"
nm_copy_sd "src/basic/random-util.c"
nm_copy_sd "src/basic/random-util.h"
nm_copy_sd "src/basic/refcnt.h"
nm_copy_sd "src/basic/set.h"
nm_copy_sd "src/basic/siphash24.c"
nm_copy_sd "src/basic/siphash24.h"
nm_copy_sd "src/basic/socket-util.h"
nm_copy_sd "src/basic/sparse-endian.h"
nm_copy_sd "src/basic/string-table.c"
nm_copy_sd "src/basic/string-table.h"
nm_copy_sd "src/basic/string-util.c"
nm_copy_sd "src/basic/string-util.h"
nm_copy_sd "src/basic/strv.c"
nm_copy_sd "src/basic/strv.h"
nm_copy_sd "src/basic/time-util.c"
nm_copy_sd "src/basic/time-util.h"
nm_copy_sd "src/basic/umask-util.h"
nm_copy_sd "src/basic/unaligned.h"
nm_copy_sd "src/basic/utf8.c"
nm_copy_sd "src/basic/utf8.h"
nm_copy_sd "src/basic/util.c"
nm_copy_sd "src/basic/util.h"
nm_copy_sd "src/libsystemd-network/arp-util.c"
nm_copy_sd "src/libsystemd-network/arp-util.h"
nm_copy_sd "src/libsystemd-network/dhcp6-internal.h"
nm_copy_sd "src/libsystemd-network/dhcp6-lease-internal.h"
nm_copy_sd "src/libsystemd-network/dhcp6-network.c"
nm_copy_sd "src/libsystemd-network/dhcp6-option.c"
nm_copy_sd "src/libsystemd-network/dhcp6-protocol.h"
nm_copy_sd "src/libsystemd-network/dhcp-identifier.c"
nm_copy_sd "src/libsystemd-network/dhcp-identifier.h"
nm_copy_sd "src/libsystemd-network/dhcp-internal.h"
nm_copy_sd "src/libsystemd-network/dhcp-lease-internal.h"
nm_copy_sd "src/libsystemd-network/dhcp-network.c"
nm_copy_sd "src/libsystemd-network/dhcp-option.c"
nm_copy_sd "src/libsystemd-network/dhcp-packet.c"
nm_copy_sd "src/libsystemd-network/dhcp-protocol.h"
nm_copy_sd "src/libsystemd-network/lldp.h"
nm_copy_sd "src/libsystemd-network/lldp-internal.h"
nm_copy_sd "src/libsystemd-network/lldp-internal.c"
nm_copy_sd "src/libsystemd-network/lldp-network.h"
nm_copy_sd "src/libsystemd-network/lldp-network.c"
nm_copy_sd "src/libsystemd-network/lldp-port.c"
nm_copy_sd "src/libsystemd-network/lldp-port.h"
nm_copy_sd "src/libsystemd-network/lldp-tlv.c"
nm_copy_sd "src/libsystemd-network/lldp-tlv.h"
nm_copy_sd "src/libsystemd-network/lldp-util.h"
nm_copy_sd "src/libsystemd-network/network-internal.c"
nm_copy_sd "src/libsystemd-network/network-internal.h"
nm_copy_sd "src/libsystemd-network/sd-dhcp6-client.c"
nm_copy_sd "src/libsystemd-network/sd-dhcp6-lease.c"
nm_copy_sd "src/libsystemd-network/sd-dhcp-client.c"
nm_copy_sd "src/libsystemd-network/sd-dhcp-lease.c"
nm_copy_sd "src/libsystemd-network/sd-ipv4ll.c"
nm_copy_sd "src/libsystemd-network/sd-ipv4acd.c"
nm_copy_sd "src/libsystemd-network/sd-lldp.c"
nm_copy_sd "src/libsystemd/sd-id128/sd-id128.c"
nm_copy_sd "src/libsystemd/sd-event/event-util.h"
nm_copy_sd "src/shared/dns-domain.c"
nm_copy_sd "src/shared/dns-domain.h"
nm_copy_sd "src/systemd/_sd-common.h"
nm_copy_sd "src/systemd/sd-dhcp6-client.h"
nm_copy_sd "src/systemd/sd-dhcp6-lease.h"
nm_copy_sd "src/systemd/sd-dhcp-client.h"
nm_copy_sd "src/systemd/sd-dhcp-lease.h"
nm_copy_sd "src/systemd/sd-event.h"
nm_copy_sd "src/systemd/sd-ndisc.h"
nm_copy_sd "src/systemd/sd-id128.h"
nm_copy_sd "src/systemd/sd-ipv4acd.h"
nm_copy_sd "src/systemd/sd-ipv4ll.h"
nm_copy_sd "src/systemd/sd-lldp.h"
85 files changed, 6363 insertions, 7962 deletions
diff --git a/src/systemd/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c new file mode 100644 index 0000000000..48183e381f --- /dev/null +++ b/src/systemd/src/basic/alloc-util.c @@ -0,0 +1,81 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "util.h" + +void* memdup(const void *p, size_t l) { + void *r; + + assert(p); + + r = malloc(l); + if (!r) + return NULL; + + memcpy(r, p, l); + return r; +} + +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; +} diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h new file mode 100644 index 0000000000..12b602e185 --- /dev/null +++ b/src/systemd/src/basic/alloc-util.h @@ -0,0 +1,108 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <alloca.h> +#include <stdlib.h> +#include <string.h> + +#include "macro.h" + +#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n))) + +#define new0(t, n) ((t*) calloc((n), sizeof(t))) + +#define newa(t, n) ((t*) alloca(sizeof(t)*(n))) + +#define newa0(t, n) ((t*) alloca0(sizeof(t)*(n))) + +#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) + +#define malloc0(n) (calloc(1, (n))) + +static inline void *mfree(void *memory) { + free(memory); + return NULL; +} + +void* memdup(const void *p, size_t l) _alloc_(2); + +static inline void freep(void *p) { + free(*(void**) p); +} + +#define _cleanup_free_ _cleanup_(freep) + +_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) { + if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) + return NULL; + + return malloc(a * b); +} + +_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) { + if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) + return NULL; + + return realloc(p, a * b); +} + +_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) { + if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) + return NULL; + + return memdup(p, a * b); +} + +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_); \ + }) diff --git a/src/systemd/src/basic/escape.c b/src/systemd/src/basic/escape.c new file mode 100644 index 0000000000..4815161b09 --- /dev/null +++ b/src/systemd/src/basic/escape.c @@ -0,0 +1,482 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "escape.h" +#include "hexdecoct.h" +#include "string-util.h" +#include "utf8.h" +#include "util.h" + +size_t cescape_char(char c, char *buf) { + char * buf_old = buf; + + 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(const char *s) { + char *r, *t; + const char *f; + + assert(s); + + /* Does C style string escaping. May be reversed with + * cunescape(). */ + + r = new(char, strlen(s)*4 + 1); + if (!r) + return NULL; + + for (f = s, t = r; *f; f++) + t += cescape_char(*f, t); + + *t = 0; + + return r; +} + +int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) { + int r = 1; + + assert(p); + assert(*p); + assert(ret); + + /* Unescapes C style. Returns the unescaped character in ret, + * unless we encountered a \u sequence in which case the full + * unicode character is returned in ret_unicode, instead. */ + + 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 = (char) ((a << 4U) | b); + r = 3; + break; + } + + case 'u': { + /* C++11 style 16bit unicode */ + + int a[4]; + unsigned 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; + + if (c < 128) + *ret = c; + else { + if (!ret_unicode) + return -EINVAL; + + *ret = 0; + *ret_unicode = c; + } + + r = 5; + break; + } + + case 'U': { + /* C++11 style 32bit unicode */ + + int a[8]; + unsigned i; + uint32_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; + + if (c < 128) + *ret = c; + else { + if (!ret_unicode) + return -EINVAL; + + *ret = 0; + *ret_unicode = 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; + uint32_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; + 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 = prefix ? strlen(prefix) : 0; + + 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; + uint32_t u; + char c; + int k; + + remaining = s + length - f; + assert(remaining > 0); + + if (*f != '\\') { + /* A literal 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, &c, &u); + if (k < 0) { + if (flags & UNESCAPE_RELAX) { + /* Invalid escape code, let's take it literal then */ + *(t++) = '\\'; + continue; + } + + free(r); + return k; + } + + if (c != 0) + /* Non-Unicode? Let's encode this directly */ + *(t++) = c; + else + /* Unicode? Then let's encode this in UTF-8 */ + t += utf8_encode_unichar(t, u); + + f += k; + } + + *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; +} + +static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) { + assert(bad); + + for (; *s; s++) { + 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); + *t = 0; + + return r; +} + +char *shell_maybe_quote(const char *s) { + const char *p; + char *r, *t; + + assert(s); + + /* Encloses a string in double quotes if necessary to make it + * OK as shell string. */ + + for (p = s; *p; p++) + if (*p <= ' ' || + *p >= 127 || + strchr(SHELL_NEED_QUOTES, *p)) + break; + + if (!*p) + return strdup(s); + + r = new(char, 1+strlen(s)*2+1+1); + if (!r) + return NULL; + + t = r; + *(t++) = '"'; + t = mempcpy(t, s, p - s); + + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE); + + *(t++)= '"'; + *t = 0; + + return r; +} diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h new file mode 100644 index 0000000000..30604c58f9 --- /dev/null +++ b/src/systemd/src/basic/escape.h @@ -0,0 +1,48 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <inttypes.h> +#include <sys/types.h> + +/* What characters are special in the shell? */ +/* must be escaped outside and inside double-quotes */ +#define SHELL_NEED_ESCAPE "\"\\`$" +/* can be escaped or double-quoted */ +#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;" + +typedef enum UnescapeFlags { + UNESCAPE_RELAX = 1, +} UnescapeFlags; + +char *cescape(const char *s); +size_t 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, char *ret, uint32_t *ret_unicode); + +char *xescape(const char *s, const char *bad); + +char *shell_escape(const char *s, const char *bad); +char *shell_maybe_quote(const char *s); diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c new file mode 100644 index 0000000000..d1b1db3a4d --- /dev/null +++ b/src/systemd/src/basic/fd-util.c @@ -0,0 +1,351 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "dirent-util.h" +#include "fd-util.h" +#include "parse-util.h" +#include "socket-util.h" +#include "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[], unsigned n_fd) { + unsigned 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; +} + +_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) { + unsigned 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[], unsigned 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) { + int fd; + struct rlimit rl; + + /* 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); + for (fd = 3; fd < (int) rl.rlim_max; fd ++) { + + if (fd_in_set(fd, except, n_except)) + continue; + + if (close_nointr(fd) < 0) + if (errno != EBADF && r == 0) + r = -errno; + } + + return r; + } + + while ((de = readdir(d))) { + int fd = -1; + + if (hidden_file(de->d_name)) + continue; + + 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; + + if (close_nointr(fd) < 0) { + /* Valgrind has its own FD and doesn't want to have it closed */ + if (errno != EBADF && r == 0) + r = -errno; + } + } + + 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(); + r = kcmp(pid, pid, KCMP_FILE, a, b); + if (r == 0) + return true; + if (r > 0) + return false; + if (errno != ENOSYS) + 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; +} + +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; +} diff --git a/src/systemd/src/basic/fd-util.h b/src/systemd/src/basic/fd-util.h new file mode 100644 index 0000000000..5ce1592eeb --- /dev/null +++ b/src/systemd/src/basic/fd-util.h @@ -0,0 +1,75 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#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[]); + +void close_many(const int fds[], unsigned 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[], unsigned n_except); + +int same_fd(int a, int b); + +void cmsg_close_all(struct msghdr *mh); + +bool fdname_is_valid(const char *s); diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c index 13a85e1158..10aacdc56d 100644 --- a/src/systemd/src/basic/fileio.c +++ b/src/systemd/src/basic/fileio.c @@ -21,11 +21,22 @@ #include <unistd.h> -#include "util.h" -#include "strv.h" -#include "utf8.h" +#include "alloc-util.h" #include "ctype.h" +#include "escape.h" +#include "fd-util.h" #include "fileio.h" +#include "fs-util.h" +#include "hexdecoct.h" +#include "parse-util.h" +#include "path-util.h" +#include "random-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "umask-util.h" +#include "utf8.h" +#include "util.h" int write_string_stream(FILE *f, const char *line, bool enforce_newline) { @@ -51,7 +62,7 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor if (r < 0) return r; - fchmod_umask(fileno(f), 0644); + (void) fchmod_umask(fileno(f), 0644); r = write_string_stream(f, line, enforce_newline); if (r >= 0) { @@ -60,13 +71,14 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor } if (r < 0) - unlink(p); + (void) unlink(p); return r; } int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { _cleanup_fclose_ FILE *f = NULL; + int q, r; assert(fn); assert(line); @@ -74,30 +86,58 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla if (flags & WRITE_STRING_FILE_ATOMIC) { assert(flags & WRITE_STRING_FILE_CREATE); - return write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + r = write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + if (r < 0) + goto fail; + + return r; } if (flags & WRITE_STRING_FILE_CREATE) { f = fopen(fn, "we"); - if (!f) - return -errno; + 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); - if (fd < 0) - return -errno; + if (fd < 0) { + r = -errno; + goto fail; + } f = fdopen(fd, "we"); if (!f) { + r = -errno; safe_close(fd); - return -errno; + goto fail; } } - return write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + r = write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + 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 read_one_line_file(const char *fn, char **line) { @@ -128,15 +168,41 @@ int read_one_line_file(const char *fn, char **line) { return 0; } -int verify_one_line_file(const char *fn, const char *line) { - _cleanup_free_ char *value = NULL; - int r; +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; - r = read_one_line_file(fn, &value); - if (r < 0) - return r; + 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; - return streq(value, line); + f = fopen(fn, "re"); + if (!f) + return -errno; + + /* 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; } int read_full_stream(FILE *f, char **contents, size_t *size) { @@ -845,3 +911,332 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin *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; +} + +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, NULL); + else + p = strjoin(*i, "/", path, NULL); + 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); +} + +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, O_WRONLY|O_CLOEXEC); + if (fd < 0) { + free(t); + return -errno; + } + + f = fdopen(fd, "we"); + if (!f) { + unlink_noerrno(t); + free(t); + safe_close(fd); + return -errno; + } + + *_f = f; + *_temp_path = t; + + return 0; +} + +int fflush_and_check(FILE *f) { + assert(f); + + errno = 0; + fflush(f); + + if (ferror(f)) + return errno ? -errno : -EIO; + + return 0; +} + +/* This is much like like mkostemp() but is subject to umask(). */ +int mkostemp_safe(char *pattern, int flags) { + _cleanup_umask_ mode_t u; + int fd; + + assert(pattern); + + u = umask(077); + + fd = mkostemp(pattern, flags); + if (fd < 0) + return -errno; + + return fd; +} + +int open_tmpfile(const char *path, int flags) { + char *p; + int fd; + + assert(path); + +#ifdef O_TMPFILE + /* Try O_TMPFILE first, if it is supported */ + fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); + if (fd >= 0) + return fd; +#endif + + /* Fall back to unguessable name + unlinking */ + p = strjoina(path, "/systemd-tmp-XXXXXX"); + + fd = mkostemp_safe(p, flags); + if (fd < 0) + return fd; + + unlink(p); + return fd; +} + +int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { + const char *fn; + char *t; + + assert(p); + assert(ret); + + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.#<extra>waldoXXXXXX + */ + + fn = basename(p); + if (!filename_is_valid(fn)) + return -EINVAL; + + if (extra == NULL) + 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_kill_slashes(t); + return 0; +} + +int tempfn_random(const char *p, const char *extra, char **ret) { + const char *fn; + char *t, *x; + uint64_t u; + unsigned i; + + assert(p); + assert(ret); + + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.#<extra>waldobaa2a261115984a9 + */ + + fn = basename(p); + if (!filename_is_valid(fn)) + return -EINVAL; + + if (!extra) + 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_kill_slashes(t); + return 0; +} + +int tempfn_random_child(const char *p, const char *extra, char **ret) { + char *t, *x; + uint64_t u; + unsigned i; + + assert(p); + assert(ret); + + /* Turns this: + * /foo/bar/waldo + * Into this: + * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0 + */ + + if (!extra) + extra = ""; + + t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); + if (!t) + return -ENOMEM; + + 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_kill_slashes(t); + 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; +} diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h index 4998d4d042..95e8698941 100644 --- a/src/systemd/src/basic/fileio.h +++ b/src/systemd/src/basic/fileio.h @@ -20,15 +20,21 @@ You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ + +#include <dirent.h> +#include <stdbool.h> #include <stddef.h> #include <stdio.h> +#include <sys/types.h> #include "macro.h" +#include "time-util.h" typedef enum { WRITE_STRING_FILE_CREATE = 1, WRITE_STRING_FILE_ATOMIC = 2, WRITE_STRING_FILE_AVOID_NEWLINE = 4, + WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8, } WriteStringFileFlags; int write_string_stream(FILE *f, const char *line, bool enforce_newline); @@ -38,7 +44,7 @@ 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_one_line_file(const char *fn, const char *line); +int verify_file(const char *fn, const char *blob, bool accept_extra_nl); int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; int load_env_file(FILE *f, const char *fname, const char *separator, char ***l); @@ -49,3 +55,30 @@ int write_env_file(const char *fname, char **l); int executable_is_script(const char *path, char **interpreter); int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); + +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); + +#define FOREACH_LINE(line, f, on_error) \ + for (;;) \ + if (!fgets(line, sizeof(line), f)) { \ + if (ferror(f)) { \ + on_error; \ + } \ + break; \ + } else + +int fflush_and_check(FILE *f); + +int fopen_temporary(const char *path, FILE **_f, char **_temp_path); +int mkostemp_safe(char *pattern, int flags); +int open_tmpfile(const char *path, int flags); + +int tempfn_xxxxxx(const char *p, const char *extra, char **ret); +int tempfn_random(const char *p, const char *extra, char **ret); +int tempfn_random_child(const char *p, const char *extra, char **ret); + +int write_timestamp_file_atomic(const char *fn, usec_t n); +int read_timestamp_file(const char *fn, usec_t *ret); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c new file mode 100644 index 0000000000..2b6189ad90 --- /dev/null +++ b/src/systemd/src/basic/fs-util.c @@ -0,0 +1,500 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "mkdir.h" +#include "parse-util.h" +#include "path-util.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" +#include "util.h" + +int unlink_noerrno(const char *path) { + PROTECT_ERRNO; + int r; + + r = unlink(path); + if (r < 0) + return -errno; + + return 0; +} + +int rmdir_parents(const char *path, const char *stop) { + size_t l; + int r = 0; + + assert(path); + assert(stop); + + l = strlen(path); + + /* Skip trailing slashes */ + while (l > 0 && path[l-1] == '/') + l--; + + while (l > 0) { + char *t; + + /* Skip last component */ + while (l > 0 && path[l-1] != '/') + l--; + + /* Skip trailing slashes */ + while (l > 0 && path[l-1] == '/') + l--; + + if (l <= 0) + break; + + t = strndup(path, l); + if (!t) + return -ENOMEM; + + if (path_startswith(stop, t)) { + free(t); + return 0; + } + + r = rmdir(t); + free(t); + + if (r < 0) + if (errno != ENOENT) + return -errno; + } + + return 0; +} + + +int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { + struct stat buf; + int ret; + + ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE); + if (ret >= 0) + return 0; + + /* renameat2() exists since Linux 3.15, btrfs added support for it later. + * If it is not implemented, fallback to another method. */ + if (!IN_SET(errno, EINVAL, ENOSYS)) + return -errno; + + /* The link()/unlink() fallback does not work on directories. But + * renameat() without RENAME_NOREPLACE gives the same semantics on + * directories, except when newpath is an *empty* directory. This is + * good enough. */ + ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW); + if (ret >= 0 && S_ISDIR(buf.st_mode)) { + ret = renameat(olddirfd, oldpath, newdirfd, newpath); + return ret >= 0 ? 0 : -errno; + } + + /* If it is not a directory, use the link()/unlink() fallback. */ + ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0); + if (ret < 0) + return -errno; + + ret = unlinkat(olddirfd, oldpath, 0); + if (ret < 0) { + /* backup errno before the following unlinkat() alters it */ + ret = errno; + (void) unlinkat(newdirfd, newpath, 0); + errno = ret; + return -errno; + } + + return 0; +} + +int readlinkat_malloc(int fd, const char *p, char **ret) { + size_t l = 100; + int r; + + assert(p); + assert(ret); + + for (;;) { + char *c; + ssize_t n; + + c = new(char, l); + if (!c) + return -ENOMEM; + + n = readlinkat(fd, p, c, l-1); + if (n < 0) { + r = -errno; + free(c); + return r; + } + + if ((size_t) n < l-1) { + c[n] = 0; + *ret = c; + return 0; + } + + free(c); + l *= 2; + } +} + +int readlink_malloc(const char *p, char **ret) { + return readlinkat_malloc(AT_FDCWD, p, ret); +} + +int readlink_value(const char *p, char **ret) { + _cleanup_free_ char *link = NULL; + char *value; + int r; + + r = readlink_malloc(p, &link); + if (r < 0) + return r; + + value = basename(link); + if (!value) + return -ENOENT; + + value = strdup(value); + if (!value) + return -ENOMEM; + + *ret = value; + + return 0; +} + +int readlink_and_make_absolute(const char *p, char **r) { + _cleanup_free_ char *target = NULL; + char *k; + int j; + + assert(p); + assert(r); + + j = readlink_malloc(p, &target); + if (j < 0) + return j; + + k = file_in_same_dir(p, target); + if (!k) + return -ENOMEM; + + *r = k; + return 0; +} + +int readlink_and_canonicalize(const char *p, char **r) { + char *t, *s; + int j; + + assert(p); + assert(r); + + j = readlink_and_make_absolute(p, &t); + if (j < 0) + return j; + + s = canonicalize_file_name(t); + if (s) { + free(t); + *r = s; + } else + *r = t; + + path_kill_slashes(*r); + + return 0; +} + +int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) { + _cleanup_free_ char *target = NULL, *t = NULL; + const char *full; + int r; + + full = prefix_roota(root, path); + r = readlink_malloc(full, &target); + if (r < 0) + return r; + + t = file_in_same_dir(path, target); + if (!t) + return -ENOMEM; + + *ret = t; + t = NULL; + + return 0; +} + +int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { + assert(path); + + /* Under the assumption that we are running privileged we + * first change the access mode and only then hand out + * ownership to avoid a window where access is too open. */ + + if (mode != MODE_INVALID) + if (chmod(path, mode) < 0) + return -errno; + + if (uid != UID_INVALID || gid != GID_INVALID) + if (chown(path, uid, gid) < 0) + return -errno; + + return 0; +} + +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { + assert(fd >= 0); + + /* Under the assumption that we are running privileged we + * first change the access mode and only then hand out + * ownership to avoid a window where access is too open. */ + + if (mode != MODE_INVALID) + if (fchmod(fd, mode) < 0) + return -errno; + + if (uid != UID_INVALID || gid != GID_INVALID) + if (fchown(fd, uid, gid) < 0) + return -errno; + + return 0; +} + +int fchmod_umask(int fd, mode_t m) { + mode_t u; + int r; + + u = umask(0777); + r = fchmod(fd, m & (~u)) < 0 ? -errno : 0; + umask(u); + + return r; +} + +int fd_warn_permissions(const char *path, int fd) { + struct stat st; + + if (fstat(fd, &st) < 0) + return -errno; + + if (st.st_mode & 0111) + log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); + + if (st.st_mode & 0002) + log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); + + if (getpid() == 1 && (st.st_mode & 0044) != 0044) + log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); + + return 0; +} + +int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { + _cleanup_close_ int fd; + int r; + + assert(path); + + if (parents) + mkdir_parents(path, 0755); + + fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644); + if (fd < 0) + return -errno; + + if (mode != MODE_INVALID) { + r = fchmod(fd, mode); + if (r < 0) + return -errno; + } + + if (uid != UID_INVALID || gid != GID_INVALID) { + r = fchown(fd, uid, gid); + if (r < 0) + return -errno; + } + + if (stamp != USEC_INFINITY) { + struct timespec ts[2]; + + timespec_store(&ts[0], stamp); + ts[1] = ts[0]; + r = futimens(fd, ts); + } else + r = futimens(fd, NULL); + if (r < 0) + return -errno; + + return 0; +} + +int touch(const char *path) { + return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); +} + +int symlink_idempotent(const char *from, const char *to) { + _cleanup_free_ char *p = NULL; + int r; + + assert(from); + assert(to); + + if (symlink(from, to) < 0) { + if (errno != EEXIST) + return -errno; + + r = readlink_malloc(to, &p); + if (r < 0) + return r; + + if (!streq(p, from)) + return -EINVAL; + } + + return 0; +} + +int symlink_atomic(const char *from, const char *to) { + _cleanup_free_ char *t = NULL; + int r; + + assert(from); + assert(to); + + r = tempfn_random(to, NULL, &t); + if (r < 0) + return r; + + if (symlink(from, t) < 0) + return -errno; + + if (rename(t, to) < 0) { + unlink_noerrno(t); + return -errno; + } + + return 0; +} + +int mknod_atomic(const char *path, mode_t mode, dev_t dev) { + _cleanup_free_ char *t = NULL; + int r; + + assert(path); + + r = tempfn_random(path, NULL, &t); + if (r < 0) + return r; + + if (mknod(t, mode, dev) < 0) + return -errno; + + if (rename(t, path) < 0) { + unlink_noerrno(t); + return -errno; + } + + return 0; +} + +int mkfifo_atomic(const char *path, mode_t mode) { + _cleanup_free_ char *t = NULL; + int r; + + assert(path); + + r = tempfn_random(path, NULL, &t); + if (r < 0) + return r; + + if (mkfifo(t, mode) < 0) + return -errno; + + if (rename(t, path) < 0) { + unlink_noerrno(t); + return -errno; + } + + return 0; +} + +int get_files_in_directory(const char *path, char ***list) { + _cleanup_closedir_ DIR *d = NULL; + size_t bufsize = 0, n = 0; + _cleanup_strv_free_ char **l = NULL; + + assert(path); + + /* Returns all files in a directory in *list, and the number + * of files as return value. If list is NULL returns only the + * number. */ + + d = opendir(path); + if (!d) + return -errno; + + for (;;) { + struct dirent *de; + + errno = 0; + de = readdir(d); + if (!de && errno != 0) + return -errno; + if (!de) + break; + + dirent_ensure_type(d, de); + + if (!dirent_is_file(de)) + continue; + + if (list) { + /* one extra slot is needed for the terminating NULL */ + if (!GREEDY_REALLOC(l, bufsize, n + 2)) + return -ENOMEM; + + l[n] = strdup(de->d_name); + if (!l[n]) + return -ENOMEM; + + l[++n] = NULL; + } else + n++; + } + + if (list) { + *list = l; + l = NULL; /* avoid freeing */ + } + + return n; +} diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h new file mode 100644 index 0000000000..5fbb7bc4c3 --- /dev/null +++ b/src/systemd/src/basic/fs-util.h @@ -0,0 +1,75 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <fcntl.h> +#include <limits.h> +#include <sys/inotify.h> +#include <sys/types.h> +#include <unistd.h> + +#include "time-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 readlink_and_canonicalize(const char *p, char **r); +int readlink_and_make_absolute_root(const char *root, const char *path, char **ret); + +int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); + +int fchmod_umask(int fd, mode_t mode); + +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); + +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 get_files_in_directory(const char *path, char ***list); + +#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]; +}; diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c index 20e7e51d9e..6e501ef6ff 100644 --- a/src/systemd/src/basic/hashmap.c +++ b/src/systemd/src/basic/hashmap.c @@ -20,18 +20,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> #include <errno.h> #include <pthread.h> +#include <stdlib.h> -#include "util.h" +#include "alloc-util.h" #include "hashmap.h" -#include "set.h" #include "macro.h" -#include "siphash24.h" -#include "strv.h" #include "mempool.h" +#include "process-util.h" #include "random-util.h" +#include "set.h" +#include "siphash24.h" +#include "strv.h" +#include "util.h" #ifdef ENABLE_DEBUG_HASHMAP #include "list.h" @@ -378,7 +380,7 @@ static unsigned base_bucket_hash(HashmapBase *h, const void *p) { h->hash_ops->hash(p, &state); - siphash24_finalize((uint8_t*)&hash, &state); + hash = siphash24_finalize(&state); return (unsigned) (hash % n_buckets(h)); } diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c new file mode 100644 index 0000000000..4eb566b15a --- /dev/null +++ b/src/systemd/src/basic/hexdecoct.c @@ -0,0 +1,698 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <ctype.h> +#include <inttypes.h> + +#include "alloc-util.h" +#include "hexdecoct.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) { + char *r, *z; + const uint8_t *x; + + z = r = malloc(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; +} + +int unhexmem(const char *p, size_t l, void **mem, size_t *len) { + _cleanup_free_ uint8_t *r = NULL; + uint8_t *z; + const char *x; + + assert(mem); + assert(len); + assert(p); + + z = r = malloc((l + 1) / 2 + 1); + if (!r) + return -ENOMEM; + + for (x = p; x < p + l; x += 2) { + int a, b; + + a = unhexchar(x[0]); + if (a < 0) + return a; + else if (x+1 < p + l) { + b = unhexchar(x[1]); + if (b < 0) + return b; + } else + b = 0; + + *(z++) = (uint8_t) a << 4 | (uint8_t) b; + } + + *z = 0; + + *mem = r; + r = NULL; + *len = (l + 1) / 2; + + return 0; +} + +/* 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; + + 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); + + /* 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 = r; + r = NULL; + *_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; +} + +char *base64mem(const void *p, size_t l) { + char *r, *z; + const uint8_t *x; + + /* 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 NULL; + + 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; + return r; +} + +int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) { + _cleanup_free_ uint8_t *r = NULL; + int a, b, c, d; + uint8_t *z; + const char *x; + size_t len; + + assert(p); + + /* padding ensures any base63 input has input divisible by 4 */ + if (l % 4 != 0) + return -EINVAL; + + /* strip the padding */ + if (l > 0 && p[l - 1] == '=') + l --; + if (l > 0 && p[l - 1] == '=') + l --; + + /* a group of four input bytes needs three output bytes, in case of + padding we need to add two or three extra bytes */ + len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0); + + z = r = malloc(len + 1); + if (!r) + return -ENOMEM; + + for (x = p; x < p + (l / 4) * 4; x += 4) { + /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ + a = unbase64char(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase64char(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase64char(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase64char(x[3]); + if (d < 0) + return -EINVAL; + + *(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 */ + } + + switch (l % 4) { + case 3: + a = unbase64char(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase64char(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase64char(x[2]); + if (c < 0) + return -EINVAL; + + /* c == 00ZZZZ00 */ + if (c & 3) + return -EINVAL; + + *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ + *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + + break; + case 2: + a = unbase64char(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase64char(x[1]); + if (b < 0) + return -EINVAL; + + /* b == 00YY0000 */ + if (b & 15) + return -EINVAL; + + *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ + + break; + case 0: + + break; + default: + return -EINVAL; + } + + *z = 0; + + *mem = r; + r = NULL; + *_len = len; + + return 0; +} + +void hexdump(FILE *f, const void *p, size_t s) { + const uint8_t *b = p; + unsigned n = 0; + + assert(s == 0 || b); + + 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; + } +} diff --git a/src/systemd/src/basic/hexdecoct.h b/src/systemd/src/basic/hexdecoct.h new file mode 100644 index 0000000000..4aeb4c3bdc --- /dev/null +++ b/src/systemd/src/basic/hexdecoct.h @@ -0,0 +1,54 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.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); + +char *base64mem(const void *p, size_t l); +int unbase64mem(const char *p, size_t l, void **mem, size_t *len); + +void hexdump(FILE *f, const void *p, size_t s); diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c index 1b816fb77a..c57a3cbd60 100644 --- a/src/systemd/src/basic/hostname-util.c +++ b/src/systemd/src/basic/hostname-util.c @@ -19,11 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/utsname.h> #include <ctype.h> +#include <sys/utsname.h> -#include "util.h" +#include "fd-util.h" +#include "fileio.h" #include "hostname-util.h" +#include "string-util.h" +#include "util.h" bool hostname_is_set(void) { struct utsname u; @@ -69,7 +72,7 @@ static bool hostname_valid_char(char c) { * 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_domain_is_valid(). + * dns_name_is_valid(). */ bool hostname_is_valid(const char *s, bool allow_trailing_dot) { unsigned n_dots = 0; diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c index d88864b598..f4e24121e7 100644 --- a/src/systemd/src/basic/in-addr-util.c +++ b/src/systemd/src/basic/in-addr-util.c @@ -21,6 +21,7 @@ #include <arpa/inet.h> +#include "alloc-util.h" #include "in-addr-util.h" int in_addr_is_null(int family, const union in_addr_union *u) { diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c new file mode 100644 index 0000000000..ac8f93ff57 --- /dev/null +++ b/src/systemd/src/basic/io-util.c @@ -0,0 +1,261 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <poll.h> +#include <unistd.h> + +#include "io-util.h" + +int flush_fd(int fd) { + struct pollfd pollfd = { + .fd = fd, + .events = POLLIN, + }; + + 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 0; + + l = read(fd, buf, sizeof(buf)); + if (l < 0) { + + if (errno == EINTR) + continue; + + if (errno == EAGAIN) + return 0; + + return -errno; + } else if (l == 0) + return 0; + } +} + +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 (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; +} diff --git a/src/systemd/src/basic/io-util.h b/src/systemd/src/basic/io-util.h new file mode 100644 index 0000000000..cd2aa75ad2 --- /dev/null +++ b/src/systemd/src/basic/io-util.h @@ -0,0 +1,76 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.h> +#include <sys/types.h> +#include <sys/uio.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); + +#define IOVEC_SET_STRING(i, s) \ + do { \ + struct iovec *_i = &(i); \ + char *_s = (char *)(s); \ + _i->iov_base = _s; \ + _i->iov_len = strlen(_s); \ + } while(false) + +static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { + unsigned j; + size_t r = 0; + + for (j = 0; j < n; j++) + r += i[j].iov_len; + + return r; +} + +static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) { + unsigned 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; +} diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h index 369d6b1127..cda1e45cc8 100644 --- a/src/systemd/src/basic/log.h +++ b/src/systemd/src/basic/log.h @@ -21,14 +21,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> +#include <errno.h> #include <stdarg.h> +#include <stdbool.h> #include <stdlib.h> -#include <syslog.h> #include <sys/signalfd.h> -#include <errno.h> +#include <syslog.h> #include "sd-id128.h" + #include "macro.h" typedef enum LogTarget{ diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h index f55d65e2f1..5088e6720d 100644 --- a/src/systemd/src/basic/macro.h +++ b/src/systemd/src/basic/macro.h @@ -22,11 +22,10 @@ ***/ #include <assert.h> -#include <sys/param.h> -#include <sys/types.h> -#include <sys/uio.h> #include <inttypes.h> #include <stdbool.h> +#include <sys/param.h> +#include <sys/types.h> #define _printf_(a,b) __attribute__ ((format (printf, a, b))) #define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) @@ -295,111 +294,10 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { #define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) #define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) -/* The following macros add 1 when converting things, since UID 0 is a - * valid UID, while the pointer NULL is special */ -#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1)) -#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) - -#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1)) -#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) - -#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p)) -#define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) - -#define memzero(x,l) (memset((x), 0, (l))) -#define zero(x) (memzero(&(x), sizeof(x))) - #define CHAR_TO_STR(x) ((char[2]) { x, 0 }) #define char_array_0(x) x[sizeof(x)-1] = 0; -#define IOVEC_SET_STRING(i, s) \ - do { \ - struct iovec *_i = &(i); \ - char *_s = (char *)(s); \ - _i->iov_base = _s; \ - _i->iov_len = strlen(_s); \ - } while(false) - -static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { - unsigned j; - size_t r = 0; - - for (j = 0; j < n; j++) - r += i[j].iov_len; - - return r; -} - -static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) { - unsigned 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; -} - -#define VA_FORMAT_ADVANCE(format, ap) \ -do { \ - int _argtypes[128]; \ - size_t _i, _k; \ - _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) - - /* 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) - /* 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 @@ -410,6 +308,15 @@ do { \ 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) \ + ans++; \ + ans; \ + }) + #define SET_FLAG(v, flag, b) \ (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag)) @@ -427,21 +334,6 @@ do { \ _found; \ }) -/* Return a nulstr for a standard cascade of configuration directories, - * suitable to pass to conf_files_list_nulstr or config_parse_many. */ -#define CONF_DIRS_NULSTR(n) \ - "/etc/" n ".d\0" \ - "/run/" n ".d\0" \ - "/usr/local/lib/" n ".d\0" \ - "/usr/lib/" n ".d\0" \ - CONF_DIR_SPLIT_USR(n) - -#ifdef HAVE_SPLIT_USR -#define CONF_DIR_SPLIT_USR(n) "/lib/" n ".d\0" -#else -#define CONF_DIR_SPLIT_USR(n) -#endif - /* Define C11 thread_local attribute even on older gcc compiler * version */ #ifndef thread_local @@ -466,10 +358,6 @@ do { \ #endif #endif -#define UID_INVALID ((uid_t) -1) -#define GID_INVALID ((gid_t) -1) -#define MODE_INVALID ((mode_t) -1) - #define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ static inline void func##p(type *p) { \ if (*p) \ @@ -477,7 +365,4 @@ do { \ } \ struct __useless_struct_to_allow_trailing_semicolon__ -#define CMSG_FOREACH(cmsg, mh) \ - for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) - #include "log.h" diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c index d5d98d8829..9ee6e6a76d 100644 --- a/src/systemd/src/basic/mempool.c +++ b/src/systemd/src/basic/mempool.c @@ -20,8 +20,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "mempool.h" #include "macro.h" +#include "mempool.h" #include "util.h" struct pool { diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c new file mode 100644 index 0000000000..3ae99d9334 --- /dev/null +++ b/src/systemd/src/basic/parse-util.c @@ -0,0 +1,528 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "extract-word.h" +#include "parse-util.h" +#include "string-util.h" +#include "util.h" + +int parse_boolean(const char *v) { + assert(v); + + 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; +} + +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 <= 0) + 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) + 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_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(base == 1000 || base == 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; +} + +char *format_bytes(char *buf, size_t l, uint64_t t) { + unsigned i; + + /* This only does IEC units so far */ + + static const struct { + const char *suffix; + uint64_t factor; + } table[] = { + { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "M", UINT64_C(1024)*UINT64_C(1024) }, + { "K", UINT64_C(1024) }, + }; + + if (t == (uint64_t) -1) + return NULL; + + for (i = 0; i < ELEMENTSOF(table); i++) { + + if (t >= table[i].factor) { + snprintf(buf, l, + "%" PRIu64 ".%" PRIu64 "%s", + t / table[i].factor, + ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10), + table[i].suffix); + + goto finish; + } + } + + snprintf(buf, l, "%" PRIu64 "B", t); + +finish: + buf[l-1] = 0; + return buf; + +} + +int safe_atou(const char *s, unsigned *ret_u) { + char *x = NULL; + unsigned long l; + + assert(s); + assert(ret_u); + + /* 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, 0); + if (errno != 0) + return -errno; + if (!x || x == s || *x) + 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) + 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) + 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) + 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) + 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(const char *s, uint16_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) + 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) + return -EINVAL; + if ((long) (int16_t) l != l) + return -ERANGE; + + *ret = (int16_t) l; + return 0; +} + +int safe_atod(const char *s, double *ret_d) { + char *x = NULL; + double d = 0; + locale_t loc; + + 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) { + freelocale(loc); + return -errno; + } + if (!x || x == s || *x) { + freelocale(loc); + return -EINVAL; + } + + freelocale(loc); + *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 limted 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; +} diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h new file mode 100644 index 0000000000..125de53d7a --- /dev/null +++ b/src/systemd/src/basic/parse-util.h @@ -0,0 +1,94 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <inttypes.h> +#include <sys/types.h> + +#include "macro.h" + +#define MODE_INVALID ((mode_t) -1) + +int parse_boolean(const char *v) _pure_; +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_size(const char *t, uint64_t base, uint64_t *size); +int parse_range(const char *t, unsigned *lower, unsigned *upper); + +#define FORMAT_BYTES_MAX 8 +char *format_bytes(char *buf, size_t l, uint64_t t); + +int safe_atou(const char *s, unsigned *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(const char *s, uint16_t *ret); +int safe_atoi16(const char *s, int16_t *ret); + +static inline int safe_atou32(const char *s, uint32_t *ret_u) { + 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 + +int safe_atod(const char *s, double *ret_d); + +int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c index 5cbfc145a4..ec90c432a4 100644 --- a/src/systemd/src/basic/path-util.c +++ b/src/systemd/src/basic/path-util.c @@ -19,21 +19,33 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <string.h> -#include <unistd.h> #include <errno.h> -#include <stdlib.h> -#include <stdio.h> #include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/statvfs.h> +#include <unistd.h> -#include "macro.h" -#include "util.h" +/* When we include libgen.h because we need dirname() we immediately + * undefine basename() since libgen.h defines it as a macro to the + * POSIX version which is really broken. We prefer GNU basename(). */ +#include <libgen.h> +#undef basename + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" #include "log.h" -#include "strv.h" -#include "path-util.h" +#include "macro.h" #include "missing.h" -#include "fileio.h" +#include "parse-util.h" +#include "path-util.h" +#include "stat-util.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" bool path_is_absolute(const char *p) { return p[0] == '/'; @@ -43,61 +55,25 @@ bool is_path(const char *p) { return !!strchr(p, '/'); } -int path_get_parent(const char *path, char **_r) { - const char *e, *a = NULL, *b = NULL, *p; - char *r; - bool slash = false; - - assert(path); - assert(_r); - - if (!*path) - return -EINVAL; - - for (e = path; *e; e++) { - - if (!slash && *e == '/') { - a = b; - b = e; - slash = true; - } else if (slash && *e != '/') - slash = false; - } - - if (*(e-1) == '/') - p = a; - else - p = b; - - if (!p) - return -EINVAL; - - if (p == path) - r = strdup("/"); - else - r = strndup(path, p-path); - - if (!r) - return -ENOMEM; - - *_r = r; - return 0; -} - -char **path_split_and_make_absolute(const char *p) { +int path_split_and_make_absolute(const char *p, char ***ret) { char **l; + int r; + assert(p); + assert(ret); l = strv_split(p, ":"); if (!l) - return NULL; + return -ENOMEM; - if (!path_strv_make_absolute_cwd(l)) { + r = path_strv_make_absolute_cwd(l); + if (r < 0) { strv_free(l); - return NULL; + return r; } - return l; + *ret = l; + return r; } char *path_make_absolute(const char *p, const char *prefix) { @@ -112,22 +88,31 @@ char *path_make_absolute(const char *p, const char *prefix) { return strjoin(prefix, "/", p, NULL); } -char *path_make_absolute_cwd(const char *p) { - _cleanup_free_ char *cwd = NULL; +int path_make_absolute_cwd(const char *p, char **ret) { + char *c; assert(p); + assert(ret); /* Similar to path_make_absolute(), but prefixes with the * current working directory. */ if (path_is_absolute(p)) - return strdup(p); + c = strdup(p); + else { + _cleanup_free_ char *cwd = NULL; - cwd = get_current_dir_name(); - if (!cwd) - return NULL; + cwd = get_current_dir_name(); + if (!cwd) + return -errno; + + c = strjoin(cwd, "/", p, NULL); + } + if (!c) + return -ENOMEM; - return strjoin(cwd, "/", p, NULL); + *ret = c; + return 0; } int path_make_relative(const char *from_dir, const char *to_path, char **_r) { @@ -215,8 +200,9 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { return 0; } -char **path_strv_make_absolute_cwd(char **l) { +int path_strv_make_absolute_cwd(char **l) { char **s; + int r; /* Goes through every item in the string list and makes it * absolute. This works in place and won't rollback any @@ -225,15 +211,15 @@ char **path_strv_make_absolute_cwd(char **l) { STRV_FOREACH(s, l) { char *t; - t = path_make_absolute_cwd(*s); - if (!t) - return NULL; + r = path_make_absolute_cwd(*s, &t); + if (r < 0) + return r; free(*s); *s = t; } - return l; + return 0; } char **path_strv_resolve(char **l, const char *prefix) { @@ -411,7 +397,7 @@ int path_compare(const char *a, const char *b) { * Which one is sorted before the other does not really matter. * Here a relative path is ordered before an absolute path. */ d = (a[0] == '/') - (b[0] == '/'); - if (d) + if (d != 0) return d; for (;;) { @@ -434,12 +420,12 @@ int path_compare(const char *a, const char *b) { /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ d = memcmp(a, b, MIN(j, k)); - if (d) + if (d != 0) return (d > 0) - (d < 0); /* sign of d */ /* Sort "/foo/a" before "/foo/aaa" */ d = (j > k) - (j < k); /* sign of (j - k) */ - if (d) + if (d != 0) return d; a += j; @@ -471,294 +457,66 @@ char* path_join(const char *root, const char *path, const char *rest) { NULL); } -static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) { - char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; - _cleanup_free_ char *fdinfo = NULL; - _cleanup_close_ int subfd = -1; - char *p; - int r; - - if ((flags & AT_EMPTY_PATH) && isempty(filename)) - xsprintf(path, "/proc/self/fdinfo/%i", fd); - else { - subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); - if (subfd < 0) - return -errno; - - xsprintf(path, "/proc/self/fdinfo/%i", subfd); - } - - r = read_full_file(path, &fdinfo, NULL); - if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */ - return -EOPNOTSUPP; - if (r < 0) - return -errno; - - p = startswith(fdinfo, "mnt_id:"); - if (!p) { - p = strstr(fdinfo, "\nmnt_id:"); - if (!p) /* The mnt_id field is a relatively new addition */ - return -EOPNOTSUPP; - - p += 8; - } - - p += strspn(p, WHITESPACE); - p[strcspn(p, WHITESPACE)] = 0; - - return safe_atoi(p, mnt_id); -} - -int fd_is_mount_point(int fd, const char *filename, int flags) { - union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT; - int mount_id = -1, mount_id_parent = -1; - bool nosupp = false, check_st_dev = true; - struct stat a, b; - int r; +int find_binary(const char *name, char **ret) { + int last_error, r; + const char *p; - assert(fd >= 0); - assert(filename); - - /* First we will try the name_to_handle_at() syscall, which - * tells us the mount id and an opaque file "handle". It is - * not supported everywhere though (kernel compile-time - * option, not all file systems are hooked up). If it works - * the mount id is usually good enough to tell us whether - * something is a mount point. - * - * If that didn't work we will try to read the mount id from - * /proc/self/fdinfo/<fd>. This is almost as good as - * name_to_handle_at(), however, does not return the - * opaque file handle. The opaque file handle is pretty useful - * to detect the root directory, which we should always - * consider a mount point. Hence we use this only as - * fallback. Exporting the mnt_id in fdinfo is a pretty recent - * kernel addition. - * - * As last fallback we do traditional fstat() based st_dev - * comparisons. This is how things were traditionally done, - * but unionfs breaks breaks this since it exposes file - * systems with a variety of st_dev reported. Also, btrfs - * subvolumes have different st_dev, even though they aren't - * real mounts of their own. */ - - r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags); - if (r < 0) { - if (errno == ENOSYS) - /* This kernel does not support name_to_handle_at() - * fall back to simpler logic. */ - goto fallback_fdinfo; - else if (errno == EOPNOTSUPP) - /* This kernel or file system does not support - * name_to_handle_at(), hence let's see if the - * upper fs supports it (in which case it is a - * mount point), otherwise fallback to the - * traditional stat() logic */ - nosupp = true; - else - return -errno; - } + assert(name); - r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH); - if (r < 0) { - if (errno == EOPNOTSUPP) { - if (nosupp) - /* Neither parent nor child do name_to_handle_at()? - We have no choice but to fall back. */ - goto fallback_fdinfo; - else - /* The parent can't do name_to_handle_at() but the - * directory we are interested in can? - * If so, it must be a mount point. */ - return 1; - } else + if (is_path(name)) { + if (access(name, X_OK) < 0) return -errno; - } - - /* The parent can do name_to_handle_at() but the - * directory we are interested in can't? If so, it - * must be a mount point. */ - if (nosupp) - return 1; - - /* If the file handle for the directory we are - * interested in and its parent are identical, we - * assume this is the root directory, which is a mount - * point. */ - - if (h.handle.handle_bytes == h_parent.handle.handle_bytes && - h.handle.handle_type == h_parent.handle.handle_type && - memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0) - return 1; - - return mount_id != mount_id_parent; - -fallback_fdinfo: - r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id); - if (r == -EOPNOTSUPP) - goto fallback_fstat; - if (r < 0) - return r; - - r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent); - if (r < 0) - return r; - - if (mount_id != mount_id_parent) - return 1; - - /* Hmm, so, the mount ids are the same. This leaves one - * special case though for the root file system. For that, - * let's see if the parent directory has the same inode as we - * are interested in. Hence, let's also do fstat() checks now, - * too, but avoid the st_dev comparisons, since they aren't - * that useful on unionfs mounts. */ - check_st_dev = false; - -fallback_fstat: - /* yay for fstatat() taking a different set of flags than the other - * _at() above */ - if (flags & AT_SYMLINK_FOLLOW) - flags &= ~AT_SYMLINK_FOLLOW; - else - flags |= AT_SYMLINK_NOFOLLOW; - if (fstatat(fd, filename, &a, flags) < 0) - return -errno; - - if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0) - return -errno; - - /* A directory with same device and inode as its parent? Must - * be the root directory */ - if (a.st_dev == b.st_dev && - a.st_ino == b.st_ino) - return 1; - - return check_st_dev && (a.st_dev != b.st_dev); -} -/* flags can be AT_SYMLINK_FOLLOW or 0 */ -int path_is_mount_point(const char *t, int flags) { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *canonical = NULL, *parent = NULL; - int r; - - assert(t); - - if (path_equal(t, "/")) - return 1; - - /* we need to resolve symlinks manually, we can't just rely on - * fd_is_mount_point() to do that for us; if we have a structure like - * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we - * look at needs to be /usr, not /. */ - if (flags & AT_SYMLINK_FOLLOW) { - canonical = canonicalize_file_name(t); - if (!canonical) - return -errno; + if (ret) { + r = path_make_absolute_cwd(name, ret); + if (r < 0) + return r; + } - t = canonical; + return 0; } - r = path_get_parent(t, &parent); - if (r < 0) - return r; - - fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH); - if (fd < 0) - return -errno; - - return fd_is_mount_point(fd, basename(t), flags); -} - -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 path_is_os_tree(const char *path) { - char *p; - int r; - - /* We use /usr/lib/os-release as flag file if something is an OS */ - p = strjoina(path, "/usr/lib/os-release"); - r = access(p, F_OK); - - if (r >= 0) - return 1; - - /* Also check for the old location in /etc, just in case. */ - p = strjoina(path, "/etc/os-release"); - r = access(p, F_OK); - - return r >= 0; -} - -int find_binary(const char *name, bool local, char **filename) { - assert(name); + /** + * Plain getenv, not secure_getenv, because we want + * to actually allow the user to pick the binary. + */ + p = getenv("PATH"); + if (!p) + p = DEFAULT_PATH; - if (is_path(name)) { - if (local && access(name, X_OK) < 0) - return -errno; + last_error = -ENOENT; - if (filename) { - char *p; + for (;;) { + _cleanup_free_ char *j = NULL, *element = NULL; - p = path_make_absolute_cwd(name); - if (!p) - return -ENOMEM; + r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + break; - *filename = p; - } + if (!path_is_absolute(element)) + continue; - return 0; - } else { - const char *path; - const char *word, *state; - size_t l; - - /** - * Plain getenv, not secure_getenv, because we want - * to actually allow the user to pick the binary. - */ - path = getenv("PATH"); - if (!path) - path = DEFAULT_PATH; - - FOREACH_WORD_SEPARATOR(word, l, path, ":", state) { - _cleanup_free_ char *p = NULL; - - if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0) - return -ENOMEM; + j = strjoin(element, "/", name, NULL); + if (!j) + return -ENOMEM; - if (access(p, X_OK) < 0) - continue; + if (access(j, X_OK) >= 0) { + /* Found it! */ - if (filename) { - *filename = path_kill_slashes(p); - p = NULL; + if (ret) { + *ret = path_kill_slashes(j); + j = NULL; } return 0; } - return -ENOENT; + last_error = -errno; } + + return last_error; } bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { @@ -796,14 +554,13 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd return changed; } -int fsck_exists(const char *fstype) { +static int binary_is_good(const char *binary) { _cleanup_free_ char *p = NULL, *d = NULL; - const char *checker; int r; - checker = strjoina("fsck.", fstype); - - r = find_binary(checker, true, &p); + r = find_binary(binary, &p); + if (r == -ENOENT) + return 0; if (r < 0) return r; @@ -811,13 +568,39 @@ int fsck_exists(const char *fstype) { * fsck */ r = readlink_malloc(p, &d); - if (r >= 0 && - (path_equal(d, "/bin/true") || - path_equal(d, "/usr/bin/true") || - path_equal(d, "/dev/null"))) - return -ENOENT; + if (r == -EINVAL) /* not a symlink */ + return 1; + if (r < 0) + return r; - return 0; + return !path_equal(d, "true") && + !path_equal(d, "/bin/true") && + !path_equal(d, "/usr/bin/true") && + !path_equal(d, "/dev/null"); +} + +int fsck_exists(const char *fstype) { + const char *checker; + + assert(fstype); + + if (streq(fstype, "auto")) + return -EINVAL; + + checker = strjoina("fsck.", fstype); + return binary_is_good(checker); +} + +int mkfs_exists(const char *fstype) { + const char *mkfs; + + assert(fstype); + + if (streq(fstype, "auto")) + return -EINVAL; + + mkfs = strjoina("mkfs.", fstype); + return binary_is_good(mkfs); } char *prefix_root(const char *root, const char *path) { @@ -853,3 +636,166 @@ char *prefix_root(const char *root, const char *path) { strcpy(p, path); return n; } + +int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) { + char *p; + int r; + + /* + * This function is intended to be used in command line + * parsers, to handle paths that are passed in. It makes the + * path absolute, and reduces it to NULL if omitted or + * root (the latter optionally). + * + * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON + * SUCCESS! Hence, do not pass in uninitialized pointers. + */ + + if (isempty(path)) { + *arg = mfree(*arg); + return 0; + } + + r = path_make_absolute_cwd(path, &p); + if (r < 0) + return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path); + + path_kill_slashes(p); + if (suppress_root && path_equal(p, "/")) + p = mfree(p); + + free(*arg); + *arg = p; + return 0; +} + +char* dirname_malloc(const char *path) { + char *d, *dir, *dir2; + + assert(path); + + d = strdup(path); + if (!d) + return NULL; + + dir = dirname(d); + assert(dir); + + if (dir == d) + return d; + + dir2 = strdup(dir); + free(d); + + return dir2; +} + +bool filename_is_valid(const char *p) { + const char *e; + + if (isempty(p)) + return false; + + if (streq(p, ".")) + return false; + + if (streq(p, "..")) + return false; + + e = strchrnul(p, '/'); + if (*e != 0) + return false; + + if (e - p > FILENAME_MAX) + return false; + + return true; +} + +bool path_is_safe(const char *p) { + + if (isempty(p)) + return false; + + if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) + return false; + + if (strlen(p)+1 > PATH_MAX) + return false; + + /* The following two checks are not really dangerous, but hey, they still are confusing */ + if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) + return false; + + if (strstr(p, "//")) + return false; + + return true; +} + +char *file_in_same_dir(const char *path, const char *filename) { + char *e, *ret; + size_t k; + + assert(path); + assert(filename); + + /* This removes the last component of path and appends + * filename, unless the latter is absolute anyway or the + * former isn't */ + + if (path_is_absolute(filename)) + return strdup(filename); + + e = strrchr(path, '/'); + if (!e) + return strdup(filename); + + k = strlen(filename); + ret = new(char, (e + 1 - path) + k + 1); + if (!ret) + return NULL; + + memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); + return ret; +} + +bool hidden_file_allow_backup(const char *filename) { + assert(filename); + + return + filename[0] == '.' || + streq(filename, "lost+found") || + streq(filename, "aquota.user") || + streq(filename, "aquota.group") || + endswith(filename, ".rpmnew") || + endswith(filename, ".rpmsave") || + endswith(filename, ".rpmorig") || + endswith(filename, ".dpkg-old") || + endswith(filename, ".dpkg-new") || + endswith(filename, ".dpkg-tmp") || + endswith(filename, ".dpkg-dist") || + endswith(filename, ".dpkg-bak") || + endswith(filename, ".dpkg-backup") || + endswith(filename, ".dpkg-remove") || + endswith(filename, ".swp"); +} + +bool hidden_file(const char *filename) { + assert(filename); + + if (endswith(filename, "~")) + return true; + + return hidden_file_allow_backup(filename); +} + +bool is_device_path(const char *path) { + + /* Returns true on paths that refer to a device, either in + * sysfs or in /dev */ + + return + path_startswith(path, "/dev/") || + path_startswith(path, "/sys/"); +} diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h index 1eac89c51b..989e0f9004 100644 --- a/src/systemd/src/basic/path-util.h +++ b/src/systemd/src/basic/path-util.h @@ -36,11 +36,10 @@ #endif bool is_path(const char *p) _pure_; -char** path_split_and_make_absolute(const char *p); -int path_get_parent(const char *path, char **parent); +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); -char* path_make_absolute_cwd(const char *p); +int path_make_absolute_cwd(const char *p, char **ret); int path_make_relative(const char *from_dir, const char *to_path, char **_r); char* path_kill_slashes(char *path); char* path_startswith(const char *path, const char *prefix) _pure_; @@ -49,20 +48,16 @@ bool path_equal(const char *a, const char *b) _pure_; bool path_equal_or_files_same(const char *a, const char *b); char* path_join(const char *root, const char *path, const char *rest); -char** path_strv_make_absolute_cwd(char **l); +int path_strv_make_absolute_cwd(char **l); char** path_strv_resolve(char **l, const char *prefix); char** path_strv_resolve_uniq(char **l, const char *prefix); -int fd_is_mount_point(int fd, const char *filename, int flags); -int path_is_mount_point(const char *path, int flags); -int path_is_read_only_fs(const char *path); -int path_is_os_tree(const char *path); - -int find_binary(const char *name, bool local, char **filename); +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 @@ -100,3 +95,17 @@ char *prefix_root(const char *root, const char *path); } \ _ret; \ }) + +int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg); + +char* dirname_malloc(const char *path); + +bool filename_is_valid(const char *p) _pure_; +bool path_is_safe(const char *p) _pure_; + +char *file_in_same_dir(const char *path, const char *filename); + +bool hidden_file_allow_backup(const char *filename); +bool hidden_file(const char *filename) _pure_; + +bool is_device_path(const char *path); diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c index d55b348c22..7590698911 100644 --- a/src/systemd/src/basic/prioq.c +++ b/src/systemd/src/basic/prioq.c @@ -29,8 +29,9 @@ * The underlying algorithm used in this implementation is a Heap. */ -#include "util.h" +#include "alloc-util.h" #include "prioq.h" +#include "util.h" struct prioq_item { void *data; diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c index b230044f50..2f5c16e2af 100644 --- a/src/systemd/src/basic/random-util.c +++ b/src/systemd/src/basic/random-util.c @@ -17,20 +17,22 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdint.h> #include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> #include <fcntl.h> -#include <time.h> +#include <linux/random.h> +#include <stdint.h> #ifdef HAVE_SYS_AUXV_H #include <sys/auxv.h> #endif -#include <linux/random.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include "fd-util.h" +#include "io-util.h" +#include "missing.h" #include "random-util.h" #include "time-util.h" -#include "missing.h" #include "util.h" int dev_urandom(void *p, size_t n) { diff --git a/src/systemd/src/basic/siphash24.c b/src/systemd/src/basic/siphash24.c index 3b61961389..10fc56da69 100644 --- a/src/systemd/src/basic/siphash24.c +++ b/src/systemd/src/basic/siphash24.c @@ -17,9 +17,9 @@ coding style) */ -#include "sparse-endian.h" - #include "siphash24.h" +#include "sparse-endian.h" +#include "unaligned.h" #include "util.h" static inline uint64_t rotate_left(uint64_t x, uint8_t b) { @@ -53,37 +53,40 @@ void siphash24_init(struct siphash *state, const uint8_t k[16]) { assert(state); assert(k); - k0 = le64toh(*(le64_t*) k); - k1 = le64toh(*(le64_t*) (k + 8)); - - /* "somepseudorandomlygeneratedbytes" */ - state->v0 = 0x736f6d6570736575ULL ^ k0; - state->v1 = 0x646f72616e646f6dULL ^ k1; - state->v2 = 0x6c7967656e657261ULL ^ k0; - state->v3 = 0x7465646279746573ULL ^ k1; - state->padding = 0; - state->inlen = 0; + k0 = unaligned_read_le64(k); + k1 = unaligned_read_le64(k + 8); + + *state = (struct siphash) { + /* "somepseudorandomlygeneratedbytes" */ + .v0 = 0x736f6d6570736575ULL ^ k0, + .v1 = 0x646f72616e646f6dULL ^ k1, + .v2 = 0x6c7967656e657261ULL ^ k0, + .v3 = 0x7465646279746573ULL ^ k1, + .padding = 0, + .inlen = 0, + }; } void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { - uint64_t m; + const uint8_t *in = _in; const uint8_t *end = in + inlen; - unsigned left = state->inlen & 7; + size_t left = state->inlen & 7; + uint64_t m; assert(in); assert(state); - /* update total length */ + /* Update total length */ state->inlen += inlen; - /* if padding exists, fill it out */ + /* If padding exists, fill it out */ if (left > 0) { - for ( ; in < end && left < 8; in ++, left ++ ) - state->padding |= ( ( uint64_t )*in ) << (left * 8); + for ( ; in < end && left < 8; in ++, left ++) + state->padding |= ((uint64_t) *in) << (left * 8); if (in == end && left < 8) - /* we did not have enough input to fill out the padding completely */ + /* We did not have enough input to fill out the padding completely */ return; #ifdef DEBUG @@ -93,6 +96,7 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding); #endif + state->v3 ^= state->padding; sipround(state); sipround(state); @@ -101,10 +105,10 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { state->padding = 0; } - end -= ( state->inlen % sizeof (uint64_t) ); + end -= (state->inlen % sizeof(uint64_t)); - for ( ; in < end; in += 8 ) { - m = le64toh(*(le64_t*) in); + for ( ; in < end; in += 8) { + m = unaligned_read_le64(in); #ifdef DEBUG printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); @@ -119,38 +123,41 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { } left = state->inlen & 7; - - switch(left) - { - case 7: state->padding |= ((uint64_t) in[6]) << 48; - - case 6: state->padding |= ((uint64_t) in[5]) << 40; - - case 5: state->padding |= ((uint64_t) in[4]) << 32; - - case 4: state->padding |= ((uint64_t) in[3]) << 24; - - case 3: state->padding |= ((uint64_t) in[2]) << 16; - - case 2: state->padding |= ((uint64_t) in[1]) << 8; - - case 1: state->padding |= ((uint64_t) in[0]); break; - - case 0: break; + switch (left) { + case 7: + state->padding |= ((uint64_t) in[6]) << 48; + case 6: + state->padding |= ((uint64_t) in[5]) << 40; + case 5: + state->padding |= ((uint64_t) in[4]) << 32; + case 4: + state->padding |= ((uint64_t) in[3]) << 24; + case 3: + state->padding |= ((uint64_t) in[2]) << 16; + case 2: + state->padding |= ((uint64_t) in[1]) << 8; + case 1: + state->padding |= ((uint64_t) in[0]); + case 0: + break; } } -void siphash24_finalize(uint8_t out[8], struct siphash *state) { +uint64_t siphash24_finalize(struct siphash *state) { uint64_t b; - b = state->padding | (( ( uint64_t )state->inlen ) << 56); + assert(state); + + b = state->padding | (((uint64_t) state->inlen) << 56); + #ifdef DEBUG - printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t)state->v0); - printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t)state->v1); - printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t)state->v2); - printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t)state->v3); + printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); + printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); + printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); + printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); printf("(%3zu) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding); #endif + state->v3 ^= b; sipround(state); sipround(state); @@ -169,14 +176,17 @@ void siphash24_finalize(uint8_t out[8], struct siphash *state) { sipround(state); sipround(state); - *(le64_t*)out = htole64(state->v0 ^ state->v1 ^ state->v2 ^ state->v3); + return state->v0 ^ state->v1 ^ state->v2 ^ state->v3; } -/* SipHash-2-4 */ -void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16]) { +uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]) { struct siphash state; + assert(in); + assert(k); + siphash24_init(&state, k); - siphash24_compress(_in, inlen, &state); - siphash24_finalize(out, &state); + siphash24_compress(in, inlen, &state); + + return siphash24_finalize(&state); } diff --git a/src/systemd/src/basic/siphash24.h b/src/systemd/src/basic/siphash24.h index 6c5cd98ee8..ba4f7d01b6 100644 --- a/src/systemd/src/basic/siphash24.h +++ b/src/systemd/src/basic/siphash24.h @@ -4,16 +4,16 @@ #include <sys/types.h> struct siphash { - uint64_t v0; - uint64_t v1; - uint64_t v2; - uint64_t v3; - uint64_t padding; - size_t inlen; + uint64_t v0; + uint64_t v1; + uint64_t v2; + uint64_t v3; + uint64_t padding; + size_t inlen; }; void siphash24_init(struct siphash *state, const uint8_t k[16]); void siphash24_compress(const void *in, size_t inlen, struct siphash *state); -void siphash24_finalize(uint8_t out[8], struct siphash *state); +uint64_t siphash24_finalize(struct siphash *state); -void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]); +uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h index 6b0ce7836f..129ffa811c 100644 --- a/src/systemd/src/basic/socket-util.h +++ b/src/systemd/src/basic/socket-util.h @@ -21,9 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/socket.h> -#include <netinet/in.h> #include <netinet/ether.h> +#include <netinet/in.h> +#include <sys/socket.h> #include <sys/un.h> #include <linux/netlink.h> #include <linux/if_packet.h> @@ -116,6 +116,17 @@ int netlink_family_from_string(const char *s) _pure_; bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b); -#define ETHER_ADDR_TO_STRING_MAX (3*6) +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); + +int getpeercred(int fd, struct ucred *ucred); +int getpeersec(int fd, char **ret); + +int send_one_fd(int transport_fd, int fd, int flags); +int receive_one_fd(int transport_fd, int flags); -char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]); +#define CMSG_FOREACH(cmsg, mh) \ + for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) diff --git a/src/systemd/src/basic/string-table.c b/src/systemd/src/basic/string-table.c new file mode 100644 index 0000000000..a860324fc9 --- /dev/null +++ b/src/systemd/src/basic/string-table.c @@ -0,0 +1,35 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "string-table.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; +} diff --git a/src/systemd/src/basic/string-table.h b/src/systemd/src/basic/string-table.h new file mode 100644 index 0000000000..51b6007214 --- /dev/null +++ b/src/systemd/src/basic/string-table.h @@ -0,0 +1,88 @@ + +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#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(name,type,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#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) + +/* For string conversions where numbers are also acceptable */ +#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ + 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; \ + } \ + 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; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c new file mode 100644 index 0000000000..6006767daa --- /dev/null +++ b/src/systemd/src/basic/string-util.c @@ -0,0 +1,800 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "gunicode.h" +#include "string-util.h" +#include "utf8.h" +#include "util.h" + +int strcmp_ptr(const char *a, const char *b) { + + /* Like strcmp(), but tries to make sense of NULL pointers */ + if (a && b) + return strcmp(a, b); + + if (!a && b) + return -1; + + if (a && !b) + return 1; + + return 0; +} + +char* endswith(const char *s, const char *postfix) { + size_t sl, pl; + + assert(s); + assert(postfix); + + sl = strlen(s); + pl = strlen(postfix); + + if (pl == 0) + return (char*) s + sl; + + if (sl < pl) + return NULL; + + if (memcmp(s + sl - pl, postfix, pl) != 0) + return NULL; + + return (char*) s + sl - pl; +} + +char* endswith_no_case(const char *s, const char *postfix) { + size_t sl, pl; + + assert(s); + assert(postfix); + + sl = strlen(s); + pl = strlen(postfix); + + if (pl == 0) + return (char*) s + sl; + + if (sl < pl) + return NULL; + + if (strcasecmp(s + sl - pl, postfix) != 0) + return NULL; + + return (char*) s + sl - pl; +} + +char* first_word(const char *s, const char *word) { + size_t sl, wl; + const char *p; + + assert(s); + assert(word); + + /* Checks if the string starts with the specified word, either + * followed by NUL or by whitespace. Returns a pointer to the + * NUL or the first character after the whitespace. */ + + sl = strlen(s); + wl = strlen(word); + + if (sl < wl) + return NULL; + + if (wl == 0) + return (char*) s; + + if (memcmp(s, word, wl) != 0) + return NULL; + + p = s + wl; + if (*p == 0) + return (char*) p; + + if (!strchr(WHITESPACE, *p)) + return NULL; + + p += strspn(p, WHITESPACE); + return (char*) p; +} + +static size_t strcspn_escaped(const char *s, const char *reject) { + bool escaped = false; + int n; + + for (n=0; s[n]; n++) { + if (escaped) + escaped = false; + else if (s[n] == '\\') + escaped = true; + else if (strchr(reject, s[n])) + break; + } + + /* if s ends in \, return index of previous char */ + return n - escaped; +} + +/* Split a string into words. */ +const char* split(const char **state, size_t *l, const char *separator, bool quoted) { + const char *current; + + current = *state; + + if (!*current) { + assert(**state == '\0'); + return NULL; + } + + current += strspn(current, separator); + if (!*current) { + *state = current; + return NULL; + } + + if (quoted && strchr("\'\"", *current)) { + char quotechars[2] = {*current, '\0'}; + + *l = strcspn_escaped(current + 1, quotechars); + if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || + (current[*l + 2] && !strchr(separator, current[*l + 2]))) { + /* right quote missing or garbage at the end */ + *state = current; + return NULL; + } + *state = current++ + *l + 2; + } else if (quoted) { + *l = strcspn_escaped(current, separator); + if (current[*l] && !strchr(separator, current[*l])) { + /* unfinished escape */ + *state = current; + return NULL; + } + *state = current + *l; + } else { + *l = strcspn(current, separator); + *state = current + *l; + } + + return current; +} + +char *strnappend(const char *s, const char *suffix, size_t b) { + size_t a; + char *r; + + if (!s && !suffix) + return strdup(""); + + if (!s) + return strndup(suffix, b); + + if (!suffix) + return strdup(s); + + assert(s); + assert(suffix); + + a = strlen(s); + if (b > ((size_t) -1) - a) + return NULL; + + r = new(char, a+b+1); + if (!r) + return NULL; + + memcpy(r, s, a); + memcpy(r+a, suffix, b); + r[a+b] = 0; + + return r; +} + +char *strappend(const char *s, const char *suffix) { + return strnappend(s, suffix, suffix ? strlen(suffix) : 0); +} + +char *strjoin(const char *x, ...) { + va_list ap; + size_t l; + char *r, *p; + + va_start(ap, x); + + if (x) { + l = strlen(x); + + for (;;) { + const char *t; + size_t n; + + t = va_arg(ap, const char *); + if (!t) + break; + + n = strlen(t); + if (n > ((size_t) -1) - l) { + va_end(ap); + return NULL; + } + + l += n; + } + } else + l = 0; + + va_end(ap); + + r = new(char, l+1); + if (!r) + return NULL; + + if (x) { + p = stpcpy(r, x); + + va_start(ap, x); + + for (;;) { + const char *t; + + t = va_arg(ap, const char *); + if (!t) + break; + + p = stpcpy(p, t); + } + + va_end(ap); + } else + r[0] = 0; + + return r; +} + +char *strstrip(char *s) { + char *e; + + /* Drops trailing whitespace. Modifies the string in + * place. Returns pointer to first non-space character */ + + s += strspn(s, WHITESPACE); + + for (e = strchr(s, 0); e > s; e --) + if (!strchr(WHITESPACE, e[-1])) + break; + + *e = 0; + + return s; +} + +char *delete_chars(char *s, const char *bad) { + char *f, *t; + + /* Drops all whitespace, regardless where in the string */ + + for (f = s, t = s; *f; f++) { + if (strchr(bad, *f)) + continue; + + *(t++) = *f; + } + + *t = 0; + + return s; +} + +char *truncate_nl(char *s) { + assert(s); + + s[strcspn(s, NEWLINE)] = 0; + return s; +} + +char *ascii_strlower(char *t) { + char *p; + + assert(t); + + for (p = t; *p; p++) + if (*p >= 'A' && *p <= 'Z') + *p = *p - 'A' + 'a'; + + return t; +} + +bool chars_intersect(const char *a, const char *b) { + const char *p; + + /* Returns true if any of the chars in a are in b. */ + for (p = a; *p; p++) + if (strchr(b, *p)) + return true; + + return false; +} + +bool string_has_cc(const char *p, const char *ok) { + const char *t; + + assert(p); + + /* + * Check if a string contains control characters. If 'ok' is + * non-NULL it may be a string containing additional CCs to be + * considered OK. + */ + + for (t = p; *t; t++) { + if (ok && strchr(ok, *t)) + continue; + + if (*t > 0 && *t < ' ') + return true; + + if (*t == 127) + return true; + } + + return false; +} + +static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { + size_t x; + char *r; + + assert(s); + assert(percent <= 100); + assert(new_length >= 3); + + if (old_length <= 3 || old_length <= new_length) + return strndup(s, old_length); + + r = new0(char, new_length+1); + if (!r) + return NULL; + + x = (new_length * percent) / 100; + + if (x > new_length - 3) + x = new_length - 3; + + memcpy(r, s, x); + r[x] = '.'; + r[x+1] = '.'; + r[x+2] = '.'; + memcpy(r + x + 3, + s + old_length - (new_length - x - 3), + new_length - x - 3); + + return r; +} + +char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { + size_t x; + char *e; + const char *i, *j; + unsigned k, len, len2; + + assert(s); + assert(percent <= 100); + assert(new_length >= 3); + + /* if no multibyte characters use ascii_ellipsize_mem for speed */ + if (ascii_is_valid(s)) + return ascii_ellipsize_mem(s, old_length, new_length, percent); + + if (old_length <= 3 || old_length <= new_length) + return strndup(s, old_length); + + x = (new_length * percent) / 100; + + if (x > new_length - 3) + x = new_length - 3; + + k = 0; + for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) { + int c; + + c = utf8_encoded_to_unichar(i); + if (c < 0) + return NULL; + k += unichar_iswide(c) ? 2 : 1; + } + + if (k > x) /* last character was wide and went over quota */ + x ++; + + for (j = s + old_length; k < new_length && j > i; ) { + int c; + + j = utf8_prev_char(j); + c = utf8_encoded_to_unichar(j); + if (c < 0) + return NULL; + k += unichar_iswide(c) ? 2 : 1; + } + assert(i <= j); + + /* we don't actually need to ellipsize */ + if (i == j) + return memdup(s, old_length + 1); + + /* make space for ellipsis */ + j = utf8_next_char(j); + + len = i - s; + len2 = s + old_length - j; + e = new(char, len + 3 + len2 + 1); + if (!e) + return NULL; + + /* + printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n", + old_length, new_length, x, len, len2, k); + */ + + memcpy(e, s, len); + e[len] = 0xe2; /* tri-dot ellipsis: … */ + e[len + 1] = 0x80; + e[len + 2] = 0xa6; + + memcpy(e + len + 3, j, len2 + 1); + + return e; +} + +char *ellipsize(const char *s, size_t length, unsigned percent) { + return ellipsize_mem(s, strlen(s), length, percent); +} + +bool nulstr_contains(const char*nulstr, const char *needle) { + const char *i; + + if (!nulstr) + return false; + + NULSTR_FOREACH(i, nulstr) + if (streq(i, needle)) + return true; + + return false; +} + +char* strshorten(char *s, size_t l) { + assert(s); + + if (l < strlen(s)) + s[l] = 0; + + return s; +} + +char *strreplace(const char *text, const char *old_string, const char *new_string) { + const char *f; + char *t, *r; + size_t l, old_len, new_len; + + assert(text); + assert(old_string); + assert(new_string); + + old_len = strlen(old_string); + new_len = strlen(new_string); + + l = strlen(text); + r = new(char, l+1); + if (!r) + return NULL; + + f = text; + t = r; + while (*f) { + char *a; + size_t d, nl; + + if (!startswith(f, old_string)) { + *(t++) = *(f++); + continue; + } + + d = t - r; + nl = l - old_len + new_len; + a = realloc(r, nl + 1); + if (!a) + goto oom; + + l = nl; + r = a; + t = r + d; + + t = stpcpy(t, new_string); + f += old_len; + } + + *t = 0; + return r; + +oom: + free(r); + return NULL; +} + +char *strip_tab_ansi(char **ibuf, size_t *_isz) { + const char *i, *begin = NULL; + enum { + STATE_OTHER, + STATE_ESCAPE, + STATE_BRACKET + } state = STATE_OTHER; + char *obuf = NULL; + size_t osz = 0, isz; + FILE *f; + + assert(ibuf); + assert(*ibuf); + + /* Strips ANSI color and replaces TABs by 8 spaces */ + + isz = _isz ? *_isz : strlen(*ibuf); + + f = open_memstream(&obuf, &osz); + if (!f) + return NULL; + + for (i = *ibuf; i < *ibuf + isz + 1; i++) { + + switch (state) { + + case STATE_OTHER: + if (i >= *ibuf + isz) /* EOT */ + break; + else if (*i == '\x1B') + state = STATE_ESCAPE; + else if (*i == '\t') + fputs(" ", f); + else + fputc(*i, f); + break; + + case STATE_ESCAPE: + if (i >= *ibuf + isz) { /* EOT */ + fputc('\x1B', f); + break; + } else if (*i == '[') { + state = STATE_BRACKET; + begin = i + 1; + } else { + fputc('\x1B', f); + fputc(*i, f); + state = STATE_OTHER; + } + + break; + + case STATE_BRACKET: + + if (i >= *ibuf + isz || /* EOT */ + (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) { + fputc('\x1B', f); + fputc('[', f); + state = STATE_OTHER; + i = begin-1; + } else if (*i == 'm') + state = STATE_OTHER; + break; + } + } + + if (ferror(f)) { + fclose(f); + free(obuf); + return NULL; + } + + fclose(f); + + free(*ibuf); + *ibuf = obuf; + + if (_isz) + *_isz = osz; + + return obuf; +} + +char *strextend(char **x, ...) { + va_list ap; + size_t f, l; + char *r, *p; + + assert(x); + + l = f = *x ? strlen(*x) : 0; + + va_start(ap, x); + for (;;) { + const char *t; + size_t n; + + t = va_arg(ap, const char *); + if (!t) + break; + + n = strlen(t); + if (n > ((size_t) -1) - l) { + va_end(ap); + return NULL; + } + + l += n; + } + va_end(ap); + + r = realloc(*x, l+1); + if (!r) + return NULL; + + p = r + f; + + va_start(ap, x); + for (;;) { + const char *t; + + t = va_arg(ap, const char *); + if (!t) + break; + + p = stpcpy(p, t); + } + va_end(ap); + + *p = 0; + *x = r; + + return r + l; +} + +char *strrep(const char *s, unsigned n) { + size_t l; + char *r, *p; + unsigned i; + + assert(s); + + l = strlen(s); + p = r = malloc(l * n + 1); + if (!r) + return NULL; + + for (i = 0; i < n; i++) + p = stpcpy(p, s); + + *p = 0; + return r; +} + +int split_pair(const char *s, const char *sep, char **l, char **r) { + char *x, *a, *b; + + assert(s); + assert(sep); + assert(l); + assert(r); + + if (isempty(sep)) + return -EINVAL; + + x = strstr(s, sep); + if (!x) + return -EINVAL; + + a = strndup(s, x - s); + if (!a) + return -ENOMEM; + + b = strdup(x + strlen(sep)); + if (!b) { + free(a); + return -ENOMEM; + } + + *l = a; + *r = b; + + return 0; +} + +int free_and_strdup(char **p, const char *s) { + char *t; + + assert(p); + + /* Replaces a string pointer with an strdup()ed new string, + * possibly freeing the old one. */ + + if (streq_ptr(*p, s)) + return 0; + + if (s) { + t = strdup(s); + if (!t) + return -ENOMEM; + } else + t = NULL; + + free(*p); + *p = t; + + return 1; +} + +#pragma GCC push_options +#pragma GCC optimize("O0") + +void* memory_erase(void *p, size_t l) { + volatile uint8_t* x = (volatile uint8_t*) p; + + /* This basically does what memset() does, but hopefully isn't + * optimized away by the compiler. One of those days, when + * glibc learns memset_s() we should replace this call by + * memset_s(), but until then this has to do. */ + + for (; l > 0; l--) + *(x++) = 'x'; + + return p; +} + +#pragma GCC pop_options + +char* string_erase(char *x) { + + if (!x) + return NULL; + + /* A delicious drop of snake-oil! To be called on memory where + * we stored passphrases or so, after we used them. */ + + return memory_erase(x, strlen(x)); +} + +char *string_free_erase(char *s) { + return mfree(string_erase(s)); +} + +bool string_is_safe(const char *p) { + const char *t; + + if (!p) + return false; + + for (t = p; *t; t++) { + if (*t > 0 && *t < ' ') /* no control characters */ + return false; + + if (strchr(QUOTES "\\\x7f", *t)) + return false; + } + + return true; +} diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h new file mode 100644 index 0000000000..54f9d3058c --- /dev/null +++ b/src/systemd/src/basic/string-util.h @@ -0,0 +1,184 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.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 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 ? s : ""; +} + +static inline const char* strnull(const char *s) { + return s ? s : "(null)"; +} + +static inline const char *strna(const char *s) { + return s ? s : "n/a"; +} + +static inline bool isempty(const char *p) { + return !p || !p[0]; +} + +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_; + +const char* split(const char **state, size_t *l, const char *separator, bool quoted); + +#define FOREACH_WORD(word, length, s, state) \ + _FOREACH_WORD(word, length, s, WHITESPACE, false, state) + +#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ + _FOREACH_WORD(word, length, s, separator, false, state) + +#define FOREACH_WORD_QUOTED(word, length, s, state) \ + _FOREACH_WORD(word, length, s, WHITESPACE, true, state) + +#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ + for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) + +char *strappend(const char *s, const char *suffix); +char *strnappend(const char *s, const char *suffix, size_t length); + +char *strjoin(const char *x, ...) _sentinel_; + +#define strjoina(a, ...) \ + ({ \ + const char *_appendees_[] = { a, __VA_ARGS__ }; \ + char *_d_, *_p_; \ + int _len_ = 0; \ + unsigned _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 *truncate_nl(char *s); + +char *ascii_strlower(char *path); + +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); +char *ellipsize(const char *s, size_t length, unsigned percent); + +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 **p, size_t *l); + +char *strextend(char **x, ...) _sentinel_; + +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); + +/* 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); +} + +void* memory_erase(void *p, size_t l); +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_; diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c index d5169467da..771781f9fc 100644 --- a/src/systemd/src/basic/strv.c +++ b/src/systemd/src/basic/strv.c @@ -19,13 +19,16 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> +#include <errno.h> #include <stdarg.h> +#include <stdlib.h> #include <string.h> -#include <errno.h> -#include "util.h" +#include "alloc-util.h" +#include "escape.h" +#include "string-util.h" #include "strv.h" +#include "util.h" char *strv_find(char **l, const char *name) { char **i; @@ -86,6 +89,15 @@ char **strv_free(char **l) { return NULL; } +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; @@ -188,17 +200,48 @@ char **strv_new(const char *x, ...) { return r; } -int strv_extend_strv(char ***a, char **b) { - int r; - char **s; +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 = realloc(*a, sizeof(char*) * (p + q + 1)); + if (!t) + return -ENOMEM; + + t[p] = NULL; + *a = t; STRV_FOREACH(s, b) { - r = strv_extend(a, *s); - if (r < 0) - return r; + + if (filter_duplicates && strv_contains(t, *s)) + continue; + + t[p+i] = strdup(*s); + if (!t[p+i]) + goto rollback; + + i++; + t[p+i] = NULL; } - return 0; + 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) { @@ -618,6 +661,41 @@ char **strv_split_nulstr(const char *s) { return r; } +int strv_make_nulstr(char **l, char **p, size_t *q) { + size_t n_allocated = 0, n = 0; + _cleanup_free_ char *m = NULL; + char **i; + + assert(p); + assert(q); + + STRV_FOREACH(i, l) { + size_t z; + + z = strlen(*i); + + if (!GREEDY_REALLOC(m, n_allocated, n + z + 1)) + return -ENOMEM; + + memcpy(m + n, *i, z + 1); + n += z + 1; + } + + if (!m) { + m = new0(char, 1); + if (!m) + return -ENOMEM; + n = 0; + } + + *p = m; + *q = n; + + m = NULL; + + return 0; +} + bool strv_overlap(char **a, char **b) { char **i; @@ -644,8 +722,12 @@ char **strv_sort(char **l) { } bool strv_equal(char **a, char **b) { - if (!a || !b) - return a == 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)) @@ -780,7 +862,7 @@ int strv_extend_n(char ***l, const char *value, size_t n) { return 0; rollback: - for (j = k; j < i; i++) + for (j = k; j < i; j++) free(nl[j]); nl[k] = NULL; diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h index 7c1f80230a..e66794fc34 100644 --- a/src/systemd/src/basic/strv.h +++ b/src/systemd/src/basic/strv.h @@ -21,10 +21,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <fnmatch.h> #include <stdarg.h> #include <stdbool.h> -#include <fnmatch.h> +#include "extract-word.h" #include "util.h" char *strv_find(char **l, const char *name) _pure_; @@ -35,12 +36,16 @@ 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); unsigned strv_length(char * const *l) _pure_; -int strv_extend_strv(char ***a, char **b); +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); @@ -80,6 +85,7 @@ char *strv_join_quoted(char **l); 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_; diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c index 531931f6e1..b9da6991da 100644 --- a/src/systemd/src/basic/time-util.c +++ b/src/systemd/src/basic/time-util.c @@ -19,15 +19,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <time.h> #include <string.h> -#include <sys/timex.h> #include <sys/timerfd.h> +#include <sys/timex.h> -#include "util.h" -#include "time-util.h" +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "parse-util.h" #include "path-util.h" +#include "string-util.h" #include "strv.h" +#include "time-util.h" +#include "util.h" usec_t now(clockid_t clock_id) { struct timespec ts; @@ -205,11 +210,8 @@ static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) return NULL; sec = (time_t) (t / USEC_PER_SEC); + localtime_or_gmtime_r(&sec, &tm, utc); - if (utc) - gmtime_r(&sec, &tm); - else - localtime_r(&sec, &tm); if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0) return NULL; @@ -235,10 +237,7 @@ static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool ut return NULL; sec = (time_t) (t / USEC_PER_SEC); - if (utc) - gmtime_r(&sec, &tm); - else - localtime_r(&sec, &tm); + localtime_or_gmtime_r(&sec, &tm, utc); if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0) return NULL; @@ -325,15 +324,15 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { const char *suffix; usec_t usec; } table[] = { - { "y", USEC_PER_YEAR }, - { "month", USEC_PER_MONTH }, - { "w", USEC_PER_WEEK }, - { "d", USEC_PER_DAY }, - { "h", USEC_PER_HOUR }, - { "min", USEC_PER_MINUTE }, - { "s", USEC_PER_SEC }, - { "ms", USEC_PER_MSEC }, - { "us", 1 }, + { "y", USEC_PER_YEAR }, + { "month", USEC_PER_MONTH }, + { "w", USEC_PER_WEEK }, + { "d", USEC_PER_DAY }, + { "h", USEC_PER_HOUR }, + { "min", USEC_PER_MINUTE }, + { "s", USEC_PER_SEC }, + { "ms", USEC_PER_MSEC }, + { "us", 1 }, }; unsigned i; @@ -484,9 +483,10 @@ int parse_timestamp(const char *t, usec_t *usec) { }; const char *k; + const char *utc; struct tm tm, copy; time_t x; - usec_t plus = 0, minus = 0, ret; + usec_t x_usec, plus = 0, minus = 0, ret; int r, weekday = -1; unsigned i; @@ -511,28 +511,15 @@ int parse_timestamp(const char *t, usec_t *usec) { assert(t); assert(usec); - x = time(NULL); - assert_se(localtime_r(&x, &tm)); - tm.tm_isdst = -1; - - if (streq(t, "now")) - goto finish; - - else if (streq(t, "today")) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + if (t[0] == '@') + return parse_sec(t + 1, usec); - } else if (streq(t, "yesterday")) { - tm.tm_mday --; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + ret = now(CLOCK_REALTIME); - } else if (streq(t, "tomorrow")) { - tm.tm_mday ++; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + if (streq(t, "now")) goto finish; - } else if (t[0] == '+') { + else if (t[0] == '+') { r = parse_sec(t+1, &plus); if (r < 0) return r; @@ -546,35 +533,51 @@ int parse_timestamp(const char *t, usec_t *usec) { goto finish; - } else if (t[0] == '@') - return parse_sec(t + 1, usec); - - else if (endswith(t, " ago")) { - _cleanup_free_ char *z; - - z = strndup(t, strlen(t) - 4); - if (!z) - return -ENOMEM; + } else if ((k = endswith(t, " ago"))) { + t = strndupa(t, k - t); - r = parse_sec(z, &minus); + r = parse_sec(t, &minus); if (r < 0) return r; goto finish; - } else if (endswith(t, " left")) { - _cleanup_free_ char *z; - z = strndup(t, strlen(t) - 4); - if (!z) - return -ENOMEM; + } else if ((k = endswith(t, " left"))) { + t = strndupa(t, k - t); - r = parse_sec(z, &plus); + r = parse_sec(t, &plus); if (r < 0) return r; goto finish; } + utc = endswith_no_case(t, " UTC"); + if (utc) + t = strndupa(t, utc - t); + + x = ret / USEC_PER_SEC; + x_usec = 0; + + assert_se(localtime_or_gmtime_r(&x, &tm, utc)); + tm.tm_isdst = -1; + + if (streq(t, "today")) { + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto from_tm; + + } else if (streq(t, "yesterday")) { + tm.tm_mday --; + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto from_tm; + + } else if (streq(t, "tomorrow")) { + tm.tm_mday ++; + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto from_tm; + } + + for (i = 0; i < ELEMENTSOF(day_nr); i++) { size_t skip; @@ -592,66 +595,95 @@ int parse_timestamp(const char *t, usec_t *usec) { copy = tm; k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); - if (k && *k == 0) - goto finish; + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } tm = copy; k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); - if (k && *k == 0) - goto finish; + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } tm = copy; k = strptime(t, "%y-%m-%d %H:%M", &tm); if (k && *k == 0) { tm.tm_sec = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%Y-%m-%d %H:%M", &tm); if (k && *k == 0) { tm.tm_sec = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%y-%m-%d", &tm); if (k && *k == 0) { tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%Y-%m-%d", &tm); if (k && *k == 0) { tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%H:%M:%S", &tm); - if (k && *k == 0) - goto finish; + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } tm = copy; k = strptime(t, "%H:%M", &tm); if (k && *k == 0) { tm.tm_sec = 0; - goto finish; + goto from_tm; } return -EINVAL; -finish: - x = mktime(&tm); +parse_usec: + { + unsigned add; + + k++; + r = parse_fractional_part_u(&k, 6, &add); + if (r < 0) + return -EINVAL; + + if (*k) + return -EINVAL; + + x_usec = add; + + } + +from_tm: + x = mktime_or_timegm(&tm, utc); if (x == (time_t) -1) return -EINVAL; if (weekday >= 0 && tm.tm_wday != weekday) return -EINVAL; - ret = (usec_t) x * USEC_PER_SEC; + ret = (usec_t) x * USEC_PER_SEC + x_usec; +finish: ret += plus; if (ret > minus) ret -= minus; @@ -663,39 +695,40 @@ finish: return 0; } -int parse_sec(const char *t, usec_t *usec) { +int parse_time(const char *t, usec_t *usec, usec_t default_unit) { + static const struct { const char *suffix; usec_t usec; } table[] = { - { "seconds", USEC_PER_SEC }, - { "second", USEC_PER_SEC }, - { "sec", USEC_PER_SEC }, - { "s", USEC_PER_SEC }, + { "seconds", USEC_PER_SEC }, + { "second", USEC_PER_SEC }, + { "sec", USEC_PER_SEC }, + { "s", USEC_PER_SEC }, { "minutes", USEC_PER_MINUTE }, - { "minute", USEC_PER_MINUTE }, - { "min", USEC_PER_MINUTE }, - { "months", USEC_PER_MONTH }, - { "month", USEC_PER_MONTH }, - { "msec", USEC_PER_MSEC }, - { "ms", USEC_PER_MSEC }, - { "m", USEC_PER_MINUTE }, - { "hours", USEC_PER_HOUR }, - { "hour", USEC_PER_HOUR }, - { "hr", USEC_PER_HOUR }, - { "h", USEC_PER_HOUR }, - { "days", USEC_PER_DAY }, - { "day", USEC_PER_DAY }, - { "d", USEC_PER_DAY }, - { "weeks", USEC_PER_WEEK }, - { "week", USEC_PER_WEEK }, - { "w", USEC_PER_WEEK }, - { "years", USEC_PER_YEAR }, - { "year", USEC_PER_YEAR }, - { "y", USEC_PER_YEAR }, - { "usec", 1ULL }, - { "us", 1ULL }, - { "", USEC_PER_SEC }, /* default is sec */ + { "minute", USEC_PER_MINUTE }, + { "min", USEC_PER_MINUTE }, + { "months", USEC_PER_MONTH }, + { "month", USEC_PER_MONTH }, + { "M", USEC_PER_MONTH }, + { "msec", USEC_PER_MSEC }, + { "ms", USEC_PER_MSEC }, + { "m", USEC_PER_MINUTE }, + { "hours", USEC_PER_HOUR }, + { "hour", USEC_PER_HOUR }, + { "hr", USEC_PER_HOUR }, + { "h", USEC_PER_HOUR }, + { "days", USEC_PER_DAY }, + { "day", USEC_PER_DAY }, + { "d", USEC_PER_DAY }, + { "weeks", USEC_PER_WEEK }, + { "week", USEC_PER_WEEK }, + { "w", USEC_PER_WEEK }, + { "years", USEC_PER_YEAR }, + { "year", USEC_PER_YEAR }, + { "y", USEC_PER_YEAR }, + { "usec", 1ULL }, + { "us", 1ULL }, }; const char *p, *s; @@ -704,6 +737,7 @@ int parse_sec(const char *t, usec_t *usec) { assert(t); assert(usec); + assert(default_unit > 0); p = t; @@ -722,6 +756,7 @@ int parse_sec(const char *t, usec_t *usec) { long long l, z = 0; char *e; unsigned i, n = 0; + usec_t multiplier, k; p += strspn(p, WHITESPACE); @@ -764,21 +799,24 @@ int parse_sec(const char *t, usec_t *usec) { for (i = 0; i < ELEMENTSOF(table); i++) if (startswith(e, table[i].suffix)) { - usec_t k = (usec_t) z * table[i].usec; - - for (; n > 0; n--) - k /= 10; - - r += (usec_t) l * table[i].usec + k; + multiplier = table[i].usec; p = e + strlen(table[i].suffix); - - something = true; break; } - if (i >= ELEMENTSOF(table)) - return -EINVAL; + if (i >= ELEMENTSOF(table)) { + multiplier = default_unit; + p = e; + } + + something = true; + k = (usec_t) z * multiplier; + + for (; n > 0; n--) + k /= 10; + + r += (usec_t) l * multiplier + k; } *usec = r; @@ -786,6 +824,10 @@ int parse_sec(const char *t, usec_t *usec) { return 0; } +int parse_sec(const char *t, usec_t *usec) { + return parse_time(t, usec, USEC_PER_SEC); +} + int parse_nsec(const char *t, nsec_t *nsec) { static const struct { const char *suffix; @@ -1072,3 +1114,25 @@ int get_timezone(char **tz) { *tz = z; return 0; } + +time_t mktime_or_timegm(struct tm *tm, bool utc) { + return utc ? timegm(tm) : mktime(tm); +} + +struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) { + return utc ? gmtime_r(t, tm) : localtime_r(t, tm); +} + +unsigned long usec_to_jiffies(usec_t u) { + static thread_local unsigned long hz = 0; + long r; + + if (hz == 0) { + r = sysconf(_SC_CLK_TCK); + + assert(r > 0); + hz = (unsigned long) r; + } + + return DIV_ROUND_UP(u , USEC_PER_SEC / hz); +} diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h index 1af01541fc..0417c29cdd 100644 --- a/src/systemd/src/basic/time-util.h +++ b/src/systemd/src/basic/time-util.h @@ -21,8 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <inttypes.h> +#include <stdio.h> +#include <time.h> typedef uint64_t usec_t; typedef uint64_t nsec_t; @@ -103,6 +104,7 @@ int dual_timestamp_deserialize(const char *value, dual_timestamp *t); int parse_timestamp(const char *t, usec_t *usec); int parse_sec(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); @@ -117,3 +119,8 @@ clockid_t clock_boottime_or_monotonic(void); "xstrftime: " #buf "[] must be big enough") 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); diff --git a/src/systemd/src/basic/umask-util.h b/src/systemd/src/basic/umask-util.h new file mode 100644 index 0000000000..8ed34658b4 --- /dev/null +++ b/src/systemd/src/basic/umask-util.h @@ -0,0 +1,48 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#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) diff --git a/src/systemd/src/basic/unaligned.h b/src/systemd/src/basic/unaligned.h index d6181dd9a9..a8115eaa1f 100644 --- a/src/systemd/src/basic/unaligned.h +++ b/src/systemd/src/basic/unaligned.h @@ -21,8 +21,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <endian.h> #include <stdint.h> +/* BE */ + static inline uint16_t unaligned_read_be16(const void *_u) { const uint8_t *u = _u; @@ -64,3 +67,47 @@ static inline void unaligned_write_be64(void *_u, uint64_t a) { unaligned_write_be32(u, (uint32_t) (a >> 32)); unaligned_write_be32(u + 4, (uint32_t) a); } + +/* LE */ + +static inline uint16_t unaligned_read_le16(const void *_u) { + const uint8_t *u = _u; + + return (((uint16_t) u[1]) << 8) | + ((uint16_t) u[0]); +} + +static inline uint32_t unaligned_read_le32(const void *_u) { + const uint8_t *u = _u; + + return (((uint32_t) unaligned_read_le16(u + 2)) << 16) | + ((uint32_t) unaligned_read_le16(u)); +} + +static inline uint64_t unaligned_read_le64(const void *_u) { + const uint8_t *u = _u; + + return (((uint64_t) unaligned_read_le32(u + 4)) << 32) | + ((uint64_t) unaligned_read_le32(u)); +} + +static inline void unaligned_write_le16(void *_u, uint16_t a) { + uint8_t *u = _u; + + u[0] = (uint8_t) a; + u[1] = (uint8_t) (a >> 8); +} + +static inline void unaligned_write_le32(void *_u, uint32_t a) { + uint8_t *u = _u; + + unaligned_write_le16(u, (uint16_t) a); + unaligned_write_le16(u + 2, (uint16_t) (a >> 16)); +} + +static inline void unaligned_write_le64(void *_u, uint64_t a) { + uint8_t *u = _u; + + unaligned_write_le32(u, (uint32_t) a); + unaligned_write_le32(u + 4, (uint32_t) (a >> 32)); +} diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c index 800884ffee..b4063a4cec 100644 --- a/src/systemd/src/basic/utf8.c +++ b/src/systemd/src/basic/utf8.c @@ -44,11 +44,13 @@ */ #include <errno.h> -#include <stdlib.h> #include <inttypes.h> -#include <string.h> #include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include "alloc-util.h" +#include "hexdecoct.h" #include "utf8.h" #include "util.h" diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c index 2855993afe..58617b354a 100644 --- a/src/systemd/src/basic/util.c +++ b/src/systemd/src/basic/util.c @@ -23,15 +23,14 @@ #include <dirent.h> #include <errno.h> #include <fcntl.h> -#include <glob.h> #include <grp.h> #include <langinfo.h> #include <libintl.h> #include <limits.h> #include <linux/magic.h> +#include <linux/oom.h> #include <linux/sched.h> #include <locale.h> -#include <netinet/ip.h> #include <poll.h> #include <pwd.h> #include <sched.h> @@ -46,7 +45,6 @@ #include <sys/mount.h> #include <sys/personality.h> #include <sys/prctl.h> -#include <sys/resource.h> #include <sys/stat.h> #include <sys/statvfs.h> #include <sys/time.h> @@ -54,7 +52,6 @@ #include <sys/utsname.h> #include <sys/vfs.h> #include <sys/wait.h> -#include <sys/xattr.h> #include <syslog.h> #include <unistd.h> @@ -72,28 +69,38 @@ * otherwise conflicts with sys/mount.h. Yay, Linux is great! */ #include <linux/fs.h> +#include "alloc-util.h" #include "build.h" #include "def.h" #include "device-nodes.h" +#include "dirent-util.h" #include "env-util.h" +#include "escape.h" #include "exit-status.h" +#include "fd-util.h" #include "fileio.h" #include "formats-util.h" #include "gunicode.h" #include "hashmap.h" +#include "hexdecoct.h" #include "hostname-util.h" #include "ioprio.h" #include "log.h" #include "macro.h" #include "missing.h" #include "mkdir.h" +#include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "random-util.h" #include "signal-util.h" #include "sparse-endian.h" +#include "stat-util.h" +#include "string-table.h" +#include "string-util.h" #include "strv.h" #include "terminal-util.h" +#include "user-util.h" #include "utf8.h" #include "util.h" #include "virt.h" @@ -118,2763 +125,6 @@ size_t page_size(void) { return pgsz; } -int strcmp_ptr(const char *a, const char *b) { - - /* Like strcmp(), but tries to make sense of NULL pointers */ - if (a && b) - return strcmp(a, b); - - if (!a && b) - return -1; - - if (a && !b) - return 1; - - return 0; -} - -bool streq_ptr(const char *a, const char *b) { - return strcmp_ptr(a, b) == 0; -} - -char* endswith(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (memcmp(s + sl - pl, postfix, pl) != 0) - return NULL; - - return (char*) s + sl - pl; -} - -char* endswith_no_case(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (strcasecmp(s + sl - pl, postfix) != 0) - return NULL; - - return (char*) s + sl - pl; -} - -char* first_word(const char *s, const char *word) { - size_t sl, wl; - const char *p; - - assert(s); - assert(word); - - /* Checks if the string starts with the specified word, either - * followed by NUL or by whitespace. Returns a pointer to the - * NUL or the first character after the whitespace. */ - - sl = strlen(s); - wl = strlen(word); - - if (sl < wl) - return NULL; - - if (wl == 0) - return (char*) s; - - if (memcmp(s, word, wl) != 0) - return NULL; - - p = s + wl; - if (*p == 0) - return (char*) p; - - if (!strchr(WHITESPACE, *p)) - return NULL; - - p += strspn(p, WHITESPACE); - return (char*) p; -} - -size_t cescape_char(char c, char *buf) { - char * buf_old = buf; - - 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; -} - -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 close_many(const int fds[], unsigned n_fd) { - unsigned 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 unlink_noerrno(const char *path) { - PROTECT_ERRNO; - int r; - - r = unlink(path); - if (r < 0) - return -errno; - - return 0; -} - -int parse_boolean(const char *v) { - assert(v); - - 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; -} - -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 <= 0) - return -ERANGE; - - *ret_pid = pid; - return 0; -} - -bool uid_is_valid(uid_t uid) { - - /* Some libc APIs use UID_INVALID as special placeholder */ - if (uid == (uid_t) 0xFFFFFFFF) - return false; - - /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ - if (uid == (uid_t) 0xFFFF) - return false; - - return true; -} - -int parse_uid(const char *s, uid_t* ret_uid) { - unsigned long ul = 0; - uid_t uid; - int r; - - assert(s); - - r = safe_atolu(s, &ul); - if (r < 0) - return r; - - uid = (uid_t) ul; - - if ((unsigned long) uid != ul) - return -ERANGE; - - if (!uid_is_valid(uid)) - return -ENXIO; /* we return ENXIO instead of EINVAL - * here, to make it easy to distuingish - * invalid numeric uids invalid - * strings. */ - - if (ret_uid) - *ret_uid = uid; - - return 0; -} - -int safe_atou(const char *s, unsigned *ret_u) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(ret_u); - - errno = 0; - l = strtoul(s, &x, 0); - - if (!x || x == s || *x || errno) - return errno > 0 ? -errno : -EINVAL; - - 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 (!x || x == s || *x || errno) - return errno > 0 ? -errno : -EINVAL; - - if ((long) (int) l != l) - return -ERANGE; - - *ret_i = (int) l; - return 0; -} - -int safe_atou8(const char *s, uint8_t *ret) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(ret); - - errno = 0; - l = strtoul(s, &x, 0); - - if (!x || x == s || *x || errno) - return errno > 0 ? -errno : -EINVAL; - - if ((unsigned long) (uint8_t) l != l) - return -ERANGE; - - *ret = (uint8_t) l; - return 0; -} - -int safe_atou16(const char *s, uint16_t *ret) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(ret); - - errno = 0; - l = strtoul(s, &x, 0); - - if (!x || x == s || *x || errno) - return errno > 0 ? -errno : -EINVAL; - - 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 (!x || x == s || *x || errno) - return errno > 0 ? -errno : -EINVAL; - - if ((long) (int16_t) l != l) - return -ERANGE; - - *ret = (int16_t) 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); - - errno = 0; - l = strtoull(s, &x, 0); - - if (!x || x == s || *x || errno) - return errno ? -errno : -EINVAL; - - *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 (!x || x == s || *x || errno) - return errno ? -errno : -EINVAL; - - *ret_lli = l; - return 0; -} - -int safe_atod(const char *s, double *ret_d) { - char *x = NULL; - double d = 0; - locale_t loc; - - 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 (!x || x == s || *x || errno) { - freelocale(loc); - return errno ? -errno : -EINVAL; - } - - freelocale(loc); - *ret_d = (double) d; - return 0; -} - -static size_t strcspn_escaped(const char *s, const char *reject) { - bool escaped = false; - int n; - - for (n=0; s[n]; n++) { - if (escaped) - escaped = false; - else if (s[n] == '\\') - escaped = true; - else if (strchr(reject, s[n])) - break; - } - - /* if s ends in \, return index of previous char */ - return n - escaped; -} - -/* Split a string into words. */ -const char* split(const char **state, size_t *l, const char *separator, bool quoted) { - const char *current; - - current = *state; - - if (!*current) { - assert(**state == '\0'); - return NULL; - } - - current += strspn(current, separator); - if (!*current) { - *state = current; - return NULL; - } - - if (quoted && strchr("\'\"", *current)) { - char quotechars[2] = {*current, '\0'}; - - *l = strcspn_escaped(current + 1, quotechars); - if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || - (current[*l + 2] && !strchr(separator, current[*l + 2]))) { - /* right quote missing or garbage at the end */ - *state = current; - return NULL; - } - *state = current++ + *l + 2; - } else if (quoted) { - *l = strcspn_escaped(current, separator); - if (current[*l] && !strchr(separator, current[*l])) { - /* unfinished escape */ - *state = current; - return NULL; - } - *state = current + *l; - } else { - *l = strcspn(current, separator); - *state = current + *l; - } - - return current; -} - -int fchmod_umask(int fd, mode_t m) { - mode_t u; - int r; - - u = umask(0777); - r = fchmod(fd, m & (~u)) < 0 ? -errno : 0; - umask(u); - - return r; -} - -char *truncate_nl(char *s) { - assert(s); - - s[strcspn(s, NEWLINE)] = 0; - return s; -} - -char *strnappend(const char *s, const char *suffix, size_t b) { - size_t a; - char *r; - - if (!s && !suffix) - return strdup(""); - - if (!s) - return strndup(suffix, b); - - if (!suffix) - return strdup(s); - - assert(s); - assert(suffix); - - a = strlen(s); - if (b > ((size_t) -1) - a) - return NULL; - - r = new(char, a+b+1); - if (!r) - return NULL; - - memcpy(r, s, a); - memcpy(r+a, suffix, b); - r[a+b] = 0; - - return r; -} - -char *strappend(const char *s, const char *suffix) { - return strnappend(s, suffix, suffix ? strlen(suffix) : 0); -} - -int readlinkat_malloc(int fd, const char *p, char **ret) { - size_t l = 100; - int r; - - assert(p); - assert(ret); - - for (;;) { - char *c; - ssize_t n; - - c = new(char, l); - if (!c) - return -ENOMEM; - - n = readlinkat(fd, p, c, l-1); - if (n < 0) { - r = -errno; - free(c); - return r; - } - - if ((size_t) n < l-1) { - c[n] = 0; - *ret = c; - return 0; - } - - free(c); - l *= 2; - } -} - -int readlink_malloc(const char *p, char **ret) { - return readlinkat_malloc(AT_FDCWD, p, ret); -} - -int readlink_value(const char *p, char **ret) { - _cleanup_free_ char *link = NULL; - char *value; - int r; - - r = readlink_malloc(p, &link); - if (r < 0) - return r; - - value = basename(link); - if (!value) - return -ENOENT; - - value = strdup(value); - if (!value) - return -ENOMEM; - - *ret = value; - - return 0; -} - -int readlink_and_make_absolute(const char *p, char **r) { - _cleanup_free_ char *target = NULL; - char *k; - int j; - - assert(p); - assert(r); - - j = readlink_malloc(p, &target); - if (j < 0) - return j; - - k = file_in_same_dir(p, target); - if (!k) - return -ENOMEM; - - *r = k; - return 0; -} - -int readlink_and_canonicalize(const char *p, char **r) { - char *t, *s; - int j; - - assert(p); - assert(r); - - j = readlink_and_make_absolute(p, &t); - if (j < 0) - return j; - - s = canonicalize_file_name(t); - if (s) { - free(t); - *r = s; - } else - *r = t; - - path_kill_slashes(*r); - - return 0; -} - -char *strstrip(char *s) { - char *e; - - /* Drops trailing whitespace. Modifies the string in - * place. Returns pointer to first non-space character */ - - s += strspn(s, WHITESPACE); - - for (e = strchr(s, 0); e > s; e --) - if (!strchr(WHITESPACE, e[-1])) - break; - - *e = 0; - - return s; -} - -char *delete_chars(char *s, const char *bad) { - char *f, *t; - - /* Drops all whitespace, regardless where in the string */ - - for (f = s, t = s; *f; f++) { - if (strchr(bad, *f)) - continue; - - *(t++) = *f; - } - - *t = 0; - - return s; -} - -char *file_in_same_dir(const char *path, const char *filename) { - char *e, *ret; - size_t k; - - assert(path); - assert(filename); - - /* This removes the last component of path and appends - * filename, unless the latter is absolute anyway or the - * former isn't */ - - if (path_is_absolute(filename)) - return strdup(filename); - - e = strrchr(path, '/'); - if (!e) - return strdup(filename); - - k = strlen(filename); - ret = new(char, (e + 1 - path) + k + 1); - if (!ret) - return NULL; - - memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); - return ret; -} - -int rmdir_parents(const char *path, const char *stop) { - size_t l; - int r = 0; - - assert(path); - assert(stop); - - l = strlen(path); - - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; - - while (l > 0) { - char *t; - - /* Skip last component */ - while (l > 0 && path[l-1] != '/') - l--; - - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; - - if (l <= 0) - break; - - if (!(t = strndup(path, l))) - return -ENOMEM; - - if (path_startswith(stop, t)) { - free(t); - return 0; - } - - r = rmdir(t); - free(t); - - if (r < 0) - if (errno != ENOENT) - return -errno; - } - - return 0; -} - -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) { - char *r, *z; - const uint8_t *x; - - z = r = malloc(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; -} - -int unhexmem(const char *p, size_t l, void **mem, size_t *len) { - _cleanup_free_ uint8_t *r = NULL; - uint8_t *z; - const char *x; - - assert(mem); - assert(len); - assert(p); - - z = r = malloc((l + 1) / 2 + 1); - if (!r) - return -ENOMEM; - - for (x = p; x < p + l; x += 2) { - int a, b; - - a = unhexchar(x[0]); - if (a < 0) - return a; - else if (x+1 < p + l) { - b = unhexchar(x[1]); - if (b < 0) - return b; - } else - b = 0; - - *(z++) = (uint8_t) a << 4 | (uint8_t) b; - } - - *z = 0; - - *mem = r; - r = NULL; - *len = (l + 1) / 2; - - return 0; -} - -/* 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; - - 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); - - /* 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 = r; - r = NULL; - *_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; -} - -char *base64mem(const void *p, size_t l) { - char *r, *z; - const uint8_t *x; - - /* 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 NULL; - - 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; - return r; -} - -int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) { - _cleanup_free_ uint8_t *r = NULL; - int a, b, c, d; - uint8_t *z; - const char *x; - size_t len; - - assert(p); - - /* padding ensures any base63 input has input divisible by 4 */ - if (l % 4 != 0) - return -EINVAL; - - /* strip the padding */ - if (l > 0 && p[l - 1] == '=') - l --; - if (l > 0 && p[l - 1] == '=') - l --; - - /* a group of four input bytes needs three output bytes, in case of - padding we need to add two or three extra bytes */ - len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0); - - z = r = malloc(len + 1); - if (!r) - return -ENOMEM; - - for (x = p; x < p + (l / 4) * 4; x += 4) { - /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ - a = unbase64char(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase64char(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase64char(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase64char(x[3]); - if (d < 0) - return -EINVAL; - - *(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 */ - } - - switch (l % 4) { - case 3: - a = unbase64char(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase64char(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase64char(x[2]); - if (c < 0) - return -EINVAL; - - /* c == 00ZZZZ00 */ - if (c & 3) - return -EINVAL; - - *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ - *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ - - break; - case 2: - a = unbase64char(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase64char(x[1]); - if (b < 0) - return -EINVAL; - - /* b == 00YY0000 */ - if (b & 15) - return -EINVAL; - - *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ - - break; - case 0: - - break; - default: - return -EINVAL; - } - - *z = 0; - - *mem = r; - r = NULL; - *_len = len; - - return 0; -} - -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 *cescape(const char *s) { - char *r, *t; - const char *f; - - assert(s); - - /* Does C style string escaping. May be reversed with - * cunescape(). */ - - r = new(char, strlen(s)*4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; *f; f++) - t += cescape_char(*f, t); - - *t = 0; - - return r; -} - -static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) { - int r = 1; - - assert(p); - assert(*p); - assert(ret); - - /* Unescapes C style. Returns the unescaped character in ret, - * unless we encountered a \u sequence in which case the full - * unicode character is returned in ret_unicode, instead. */ - - 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 = (char) ((a << 4U) | b); - r = 3; - break; - } - - case 'u': { - /* C++11 style 16bit unicode */ - - int a[4]; - unsigned 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; - - if (c < 128) - *ret = c; - else { - if (!ret_unicode) - return -EINVAL; - - *ret = 0; - *ret_unicode = c; - } - - r = 5; - break; - } - - case 'U': { - /* C++11 style 32bit unicode */ - - int a[8]; - unsigned i; - uint32_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; - - if (c < 128) - *ret = c; - else { - if (!ret_unicode) - return -EINVAL; - - *ret = 0; - *ret_unicode = 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; - uint32_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; - 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 = prefix ? strlen(prefix) : 0; - - 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; - uint32_t u; - char c; - int k; - - remaining = s + length - f; - assert(remaining > 0); - - if (*f != '\\') { - /* A literal 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, &c, &u); - if (k < 0) { - if (flags & UNESCAPE_RELAX) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '\\'; - continue; - } - - free(r); - return k; - } - - if (c != 0) - /* Non-Unicode? Let's encode this directly */ - *(t++) = c; - else - /* Unicode? Then let's encode this in UTF-8 */ - t += utf8_encode_unichar(t, u); - - f += k; - } - - *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 *ascii_strlower(char *t) { - char *p; - - assert(t); - - for (p = t; *p; p++) - if (*p >= 'A' && *p <= 'Z') - *p = *p - 'A' + 'a'; - - return t; -} - -_pure_ static bool hidden_file_allow_backup(const char *filename) { - assert(filename); - - return - filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || - endswith(filename, ".rpmnew") || - endswith(filename, ".rpmsave") || - endswith(filename, ".rpmorig") || - endswith(filename, ".dpkg-old") || - endswith(filename, ".dpkg-new") || - endswith(filename, ".dpkg-tmp") || - endswith(filename, ".dpkg-dist") || - endswith(filename, ".dpkg-bak") || - endswith(filename, ".dpkg-backup") || - endswith(filename, ".dpkg-remove") || - endswith(filename, ".swp"); -} - -bool hidden_file(const char *filename) { - assert(filename); - - if (endswith(filename, "~")) - return true; - - return hidden_file_allow_backup(filename); -} - -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; -} - -_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) { - unsigned 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[], unsigned 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) { - int fd; - struct rlimit rl; - - /* 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); - for (fd = 3; fd < (int) rl.rlim_max; fd ++) { - - if (fd_in_set(fd, except, n_except)) - continue; - - if (close_nointr(fd) < 0) - if (errno != EBADF && r == 0) - r = -errno; - } - - return r; - } - - while ((de = readdir(d))) { - int fd = -1; - - if (hidden_file(de->d_name)) - continue; - - 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; - - if (close_nointr(fd) < 0) { - /* Valgrind has its own FD and doesn't want to have it closed */ - if (errno != EBADF && r == 0) - r = -errno; - } - } - - return r; -} - -bool chars_intersect(const char *a, const char *b) { - const char *p; - - /* Returns true if any of the chars in a are in b. */ - for (p = a; *p; p++) - if (strchr(b, *p)) - return true; - - return false; -} - -bool fstype_is_network(const char *fstype) { - static const char table[] = - "afs\0" - "cifs\0" - "smbfs\0" - "sshfs\0" - "ncpfs\0" - "ncp\0" - "nfs\0" - "nfs4\0" - "gfs\0" - "gfs2\0" - "glusterfs\0"; - - const char *x; - - x = startswith(fstype, "fuse."); - if (x) - fstype = x; - - return nulstr_contains(table, fstype); -} - -int flush_fd(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - - 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 0; - - l = read(fd, buf, sizeof(buf)); - if (l < 0) { - - if (errno == EINTR) - continue; - - if (errno == EAGAIN) - return 0; - - return -errno; - } else if (l == 0) - return 0; - } -} - -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]); -} - -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 (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 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(base == 1000 || base == 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); - if (*p == '-') - return -ERANGE; - - errno = 0; - l = strtoull(p, &e, 10); - if (errno > 0) - return -errno; - if (e == p) - return -EINVAL; - - 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; -} - -bool is_device_path(const char *path) { - - /* Returns true on paths that refer to a device, either in - * sysfs or in /dev */ - - return - path_startswith(path, "/dev/") || - path_startswith(path, "/sys/"); -} - -int dir_is_empty(const char *path) { - _cleanup_closedir_ DIR *d; - - d = opendir(path); - if (!d) - return -errno; - - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - return 1; - - if (!hidden_file(de->d_name)) - return 0; - } -} - -char* dirname_malloc(const char *path) { - char *d, *dir, *dir2; - - d = strdup(path); - if (!d) - return NULL; - dir = dirname(d); - assert(dir); - - if (dir != d) { - dir2 = strdup(dir); - free(d); - return dir2; - } - - return dir; -} - -void rename_process(const char name[8]) { - assert(name); - - /* This is a like a poor man's setproctitle(). It changes the - * comm field, argv[0], and also the glibc's internally used - * name of the process. For the first one a limit of 16 chars - * applies, to the second one usually one of 10 (i.e. length - * of "/sbin/init"), to the third one one of 7 (i.e. length of - * "systemd"). If you pass a longer string it will be - * truncated */ - - prctl(PR_SET_NAME, name); - - if (program_invocation_name) - strncpy(program_invocation_name, name, strlen(program_invocation_name)); - - if (saved_argc > 0) { - int i; - - if (saved_argv[0]) - strncpy(saved_argv[0], name, strlen(saved_argv[0])); - - for (i = 1; i < saved_argc; i++) { - if (!saved_argv[i]) - break; - - memzero(saved_argv[i], strlen(saved_argv[i])); - } - } -} - -char *lookup_uid(uid_t uid) { - long bufsize; - char *name; - _cleanup_free_ char *buf = NULL; - struct passwd pwbuf, *pw = NULL; - - /* Shortcut things to avoid NSS lookups */ - if (uid == 0) - return strdup("root"); - - bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); - if (bufsize <= 0) - bufsize = 4096; - - buf = malloc(bufsize); - if (!buf) - return NULL; - - if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) - return strdup(pw->pw_name); - - if (asprintf(&name, UID_FMT, uid) < 0) - return NULL; - - return name; -} - -char* getlogname_malloc(void) { - uid_t uid; - struct stat st; - - if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0) - uid = st.st_uid; - else - uid = getuid(); - - return lookup_uid(uid); -} - -char *getusername_malloc(void) { - const char *e; - - e = getenv("USER"); - if (e) - return strdup(e); - - return lookup_uid(getuid()); -} - -bool is_temporary_fs(const struct statfs *s) { - assert(s); - - return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) || - F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC); -} - -int fd_is_temporary_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_temporary_fs(&s); -} - -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { - assert(path); - - /* Under the assumption that we are running privileged we - * first change the access mode and only then hand out - * ownership to avoid a window where access is too open. */ - - if (mode != MODE_INVALID) - if (chmod(path, mode) < 0) - return -errno; - - if (uid != UID_INVALID || gid != GID_INVALID) - if (chown(path, uid, gid) < 0) - return -errno; - - return 0; -} - -int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { - assert(fd >= 0); - - /* Under the assumption that we are running privileged we - * first change the access mode and only then hand out - * ownership to avoid a window where access is too open. */ - - if (mode != MODE_INVALID) - if (fchmod(fd, mode) < 0) - return -errno; - - if (uid != UID_INVALID || gid != GID_INVALID) - if (fchown(fd, uid, gid) < 0) - return -errno; - - return 0; -} - -int files_same(const char *filea, const char *fileb) { - struct stat a, b; - - if (stat(filea, &a) < 0) - return -errno; - - if (stat(fileb, &b) < 0) - return -errno; - - return a.st_dev == b.st_dev && - a.st_ino == b.st_ino; -} - -int running_in_chroot(void) { - int ret; - - ret = files_same("/proc/1/root", "/"); - if (ret < 0) - return ret; - - return ret == 0; -} - -static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x; - char *r; - - assert(s); - assert(percent <= 100); - assert(new_length >= 3); - - if (old_length <= 3 || old_length <= new_length) - return strndup(s, old_length); - - r = new0(char, new_length+1); - if (!r) - return NULL; - - x = (new_length * percent) / 100; - - if (x > new_length - 3) - x = new_length - 3; - - memcpy(r, s, x); - r[x] = '.'; - r[x+1] = '.'; - r[x+2] = '.'; - memcpy(r + x + 3, - s + old_length - (new_length - x - 3), - new_length - x - 3); - - return r; -} - -char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x; - char *e; - const char *i, *j; - unsigned k, len, len2; - - assert(s); - assert(percent <= 100); - assert(new_length >= 3); - - /* if no multibyte characters use ascii_ellipsize_mem for speed */ - if (ascii_is_valid(s)) - return ascii_ellipsize_mem(s, old_length, new_length, percent); - - if (old_length <= 3 || old_length <= new_length) - return strndup(s, old_length); - - x = (new_length * percent) / 100; - - if (x > new_length - 3) - x = new_length - 3; - - k = 0; - for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) { - int c; - - c = utf8_encoded_to_unichar(i); - if (c < 0) - return NULL; - k += unichar_iswide(c) ? 2 : 1; - } - - if (k > x) /* last character was wide and went over quota */ - x ++; - - for (j = s + old_length; k < new_length && j > i; ) { - int c; - - j = utf8_prev_char(j); - c = utf8_encoded_to_unichar(j); - if (c < 0) - return NULL; - k += unichar_iswide(c) ? 2 : 1; - } - assert(i <= j); - - /* we don't actually need to ellipsize */ - if (i == j) - return memdup(s, old_length + 1); - - /* make space for ellipsis */ - j = utf8_next_char(j); - - len = i - s; - len2 = s + old_length - j; - e = new(char, len + 3 + len2 + 1); - if (!e) - return NULL; - - /* - printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n", - old_length, new_length, x, len, len2, k); - */ - - memcpy(e, s, len); - e[len] = 0xe2; /* tri-dot ellipsis: … */ - e[len + 1] = 0x80; - e[len + 2] = 0xa6; - - memcpy(e + len + 3, j, len2 + 1); - - return e; -} - -char *ellipsize(const char *s, size_t length, unsigned percent) { - return ellipsize_mem(s, strlen(s), length, percent); -} - -int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - _cleanup_close_ int fd; - int r; - - assert(path); - - if (parents) - mkdir_parents(path, 0755); - - fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644); - if (fd < 0) - return -errno; - - if (mode > 0) { - r = fchmod(fd, mode); - if (r < 0) - return -errno; - } - - if (uid != UID_INVALID || gid != GID_INVALID) { - r = fchown(fd, uid, gid); - if (r < 0) - return -errno; - } - - if (stamp != USEC_INFINITY) { - struct timespec ts[2]; - - timespec_store(&ts[0], stamp); - ts[1] = ts[0]; - r = futimens(fd, ts); - } else - r = futimens(fd, NULL); - if (r < 0) - return -errno; - - return 0; -} - -int touch(const char *path) { - return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0); -} - -static char *unquote(const char *s, const char* quotes) { - size_t l; - assert(s); - - /* This is rather stupid, simply removes the heading and - * trailing quotes if there is one. Doesn't care about - * escaping or anything. - * - * DON'T USE THIS FOR NEW CODE ANYMORE!*/ - - l = strlen(s); - if (l < 2) - return strdup(s); - - if (strchr(quotes, s[0]) && s[l-1] == s[0]) - return strndup(s+1, l-2); - - return strdup(s); -} - -noreturn void freeze(void) { - - /* Make sure nobody waits for us on a socket anymore */ - close_all_fds(NULL, 0); - - sync(); - - for (;;) - pause(); -} - -bool null_or_empty(struct stat *st) { - assert(st); - - if (S_ISREG(st->st_mode) && st->st_size <= 0) - return true; - - 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); -} - -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; -} - -static char *tag_to_udev_node(const char *tagvalue, const char *by) { - _cleanup_free_ char *t = NULL, *u = NULL; - size_t enc_len; - - u = unquote(tagvalue, QUOTES); - if (!u) - return NULL; - - enc_len = strlen(u) * 4 + 1; - t = new(char, enc_len); - if (!t) - return NULL; - - if (encode_devnode_name(u, t, enc_len) < 0) - return NULL; - - return strjoin("/dev/disk/by-", by, "/", t, NULL); -} - -char *fstab_node_to_udev_node(const char *p) { - assert(p); - - if (startswith(p, "LABEL=")) - return tag_to_udev_node(p+6, "label"); - - if (startswith(p, "UUID=")) - return tag_to_udev_node(p+5, "uuid"); - - if (startswith(p, "PARTUUID=")) - return tag_to_udev_node(p+9, "partuuid"); - - if (startswith(p, "PARTLABEL=")) - return tag_to_udev_node(p+10, "partlabel"); - - return strdup(p); -} - -bool dirent_is_file(const struct dirent *de) { - assert(de); - - if (hidden_file(de->d_name)) - return false; - - if (de->d_type != DT_REG && - de->d_type != DT_LNK && - de->d_type != DT_UNKNOWN) - return false; - - return true; -} - -bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { - assert(de); - - if (de->d_type != DT_REG && - de->d_type != DT_LNK && - de->d_type != DT_UNKNOWN) - return false; - - if (hidden_file_allow_backup(de->d_name)) - return false; - - return endswith(de->d_name, suffix); -} - static int do_execute(char **directories, usec_t timeout, char *argv[]) { _cleanup_hashmap_free_free_ Hashmap *pids = NULL; _cleanup_set_free_free_ Set *seen = NULL; @@ -2956,7 +206,7 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) { log_debug("Spawned %s as " PID_FMT ".", path, pid); - r = hashmap_put(pids, UINT_TO_PTR(pid), path); + r = hashmap_put(pids, PID_TO_PTR(pid), path); if (r < 0) return log_oom(); path = NULL; @@ -2974,10 +224,10 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) { _cleanup_free_ char *path = NULL; pid_t pid; - pid = PTR_TO_UINT(hashmap_first_key(pids)); + pid = PTR_TO_PID(hashmap_first_key(pids)); assert(pid > 0); - path = hashmap_remove(pids, UINT_TO_PTR(pid)); + path = hashmap_remove(pids, PID_TO_PTR(pid)); assert(path); wait_for_terminate_and_warn(path, pid, true); @@ -3015,189 +265,10 @@ void execute_directories(const char* const* directories, usec_t timeout, char *a wait_for_terminate_and_warn(name, executor_pid, true); } -bool nulstr_contains(const char*nulstr, const char *needle) { - const char *i; - - if (!nulstr) - return false; - - NULSTR_FOREACH(i, nulstr) - if (streq(i, needle)) - return true; - - return false; -} - bool plymouth_running(void) { return access("/run/plymouth/pid", F_OK) >= 0; } -char* strshorten(char *s, size_t l) { - assert(s); - - if (l < strlen(s)) - s[l] = 0; - - return s; -} - -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; -} - -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, O_WRONLY|O_CLOEXEC); - if (fd < 0) { - free(t); - return -errno; - } - - f = fdopen(fd, "we"); - if (!f) { - unlink_noerrno(t); - free(t); - safe_close(fd); - return -errno; - } - - *_f = f; - *_temp_path = t; - - return 0; -} - -int symlink_atomic(const char *from, const char *to) { - _cleanup_free_ char *t = NULL; - int r; - - assert(from); - assert(to); - - r = tempfn_random(to, NULL, &t); - if (r < 0) - return r; - - if (symlink(from, t) < 0) - return -errno; - - if (rename(t, to) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int symlink_idempotent(const char *from, const char *to) { - _cleanup_free_ char *p = NULL; - int r; - - assert(from); - assert(to); - - if (symlink(from, to) < 0) { - if (errno != EEXIST) - return -errno; - - r = readlink_malloc(to, &p); - if (r < 0) - return r; - - if (!streq(p, from)) - return -EINVAL; - } - - return 0; -} - -int mknod_atomic(const char *path, mode_t mode, dev_t dev) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - if (mknod(t, mode, dev) < 0) - return -errno; - - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mkfifo_atomic(const char *path, mode_t mode) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - if (mkfifo(t, mode) < 0) - return -errno; - - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - bool display_is_local(const char *display) { assert(display); @@ -3232,368 +303,6 @@ int socket_from_display(const char *display, char **path) { return 0; } -int get_user_creds( - const char **username, - uid_t *uid, gid_t *gid, - const char **home, - const char **shell) { - - struct passwd *p; - uid_t u; - - assert(username); - assert(*username); - - /* We enforce some special rules for uid=0: in order to avoid - * NSS lookups for root we hardcode its data. */ - - if (streq(*username, "root") || streq(*username, "0")) { - *username = "root"; - - if (uid) - *uid = 0; - - if (gid) - *gid = 0; - - if (home) - *home = "/root"; - - if (shell) - *shell = "/bin/sh"; - - return 0; - } - - if (parse_uid(*username, &u) >= 0) { - errno = 0; - p = getpwuid(u); - - /* If there are multiple users with the same id, make - * sure to leave $USER to the configured value instead - * of the first occurrence in the database. However if - * the uid was configured by a numeric uid, then let's - * pick the real username from /etc/passwd. */ - if (p) - *username = p->pw_name; - } else { - errno = 0; - p = getpwnam(*username); - } - - if (!p) - return errno > 0 ? -errno : -ESRCH; - - if (uid) - *uid = p->pw_uid; - - if (gid) - *gid = p->pw_gid; - - if (home) - *home = p->pw_dir; - - if (shell) - *shell = p->pw_shell; - - return 0; -} - -char* uid_to_name(uid_t uid) { - struct passwd *p; - char *r; - - if (uid == 0) - return strdup("root"); - - p = getpwuid(uid); - if (p) - return strdup(p->pw_name); - - if (asprintf(&r, UID_FMT, uid) < 0) - return NULL; - - return r; -} - -char* gid_to_name(gid_t gid) { - struct group *p; - char *r; - - if (gid == 0) - return strdup("root"); - - p = getgrgid(gid); - if (p) - return strdup(p->gr_name); - - if (asprintf(&r, GID_FMT, gid) < 0) - return NULL; - - return r; -} - -int get_group_creds(const char **groupname, gid_t *gid) { - struct group *g; - gid_t id; - - assert(groupname); - - /* We enforce some special rules for gid=0: in order to avoid - * NSS lookups for root we hardcode its data. */ - - if (streq(*groupname, "root") || streq(*groupname, "0")) { - *groupname = "root"; - - if (gid) - *gid = 0; - - return 0; - } - - if (parse_gid(*groupname, &id) >= 0) { - errno = 0; - g = getgrgid(id); - - if (g) - *groupname = g->gr_name; - } else { - errno = 0; - g = getgrnam(*groupname); - } - - if (!g) - return errno > 0 ? -errno : -ESRCH; - - if (gid) - *gid = g->gr_gid; - - return 0; -} - -int in_gid(gid_t gid) { - gid_t *gids; - int ngroups_max, r, i; - - if (getgid() == gid) - return 1; - - if (getegid() == gid) - return 1; - - ngroups_max = sysconf(_SC_NGROUPS_MAX); - assert(ngroups_max > 0); - - gids = alloca(sizeof(gid_t) * ngroups_max); - - r = getgroups(ngroups_max, gids); - if (r < 0) - return -errno; - - for (i = 0; i < r; i++) - if (gids[i] == gid) - return 1; - - return 0; -} - -int in_group(const char *name) { - int r; - gid_t gid; - - r = get_group_creds(&name, &gid); - if (r < 0) - return r; - - return in_gid(gid); -} - -int glob_exists(const char *path) { - _cleanup_globfree_ glob_t g = {}; - int k; - - assert(path); - - errno = 0; - k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g); - - if (k == GLOB_NOMATCH) - return 0; - else if (k == GLOB_NOSPACE) - return -ENOMEM; - else if (k == 0) - return !strv_isempty(g.gl_pathv); - else - return errno ? -errno : -EIO; -} - -int glob_extend(char ***strv, const char *path) { - _cleanup_globfree_ glob_t g = {}; - int k; - char **p; - - errno = 0; - k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g); - - if (k == GLOB_NOMATCH) - return -ENOENT; - else if (k == GLOB_NOSPACE) - return -ENOMEM; - else if (k != 0 || strv_isempty(g.gl_pathv)) - return errno ? -errno : -EIO; - - STRV_FOREACH(p, g.gl_pathv) { - k = strv_extend(strv, *p); - if (k < 0) - break; - } - - return k; -} - -int dirent_ensure_type(DIR *d, struct dirent *de) { - struct stat st; - - assert(d); - assert(de); - - if (de->d_type != DT_UNKNOWN) - return 0; - - if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) - return -errno; - - de->d_type = - S_ISREG(st.st_mode) ? DT_REG : - S_ISDIR(st.st_mode) ? DT_DIR : - S_ISLNK(st.st_mode) ? DT_LNK : - S_ISFIFO(st.st_mode) ? DT_FIFO : - S_ISSOCK(st.st_mode) ? DT_SOCK : - S_ISCHR(st.st_mode) ? DT_CHR : - S_ISBLK(st.st_mode) ? DT_BLK : - DT_UNKNOWN; - - return 0; -} - -int get_files_in_directory(const char *path, char ***list) { - _cleanup_closedir_ DIR *d = NULL; - size_t bufsize = 0, n = 0; - _cleanup_strv_free_ char **l = NULL; - - assert(path); - - /* Returns all files in a directory in *list, and the number - * of files as return value. If list is NULL returns only the - * number. */ - - d = opendir(path); - if (!d) - return -errno; - - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - if (!de) - break; - - dirent_ensure_type(d, de); - - if (!dirent_is_file(de)) - continue; - - if (list) { - /* one extra slot is needed for the terminating NULL */ - if (!GREEDY_REALLOC(l, bufsize, n + 2)) - return -ENOMEM; - - l[n] = strdup(de->d_name); - if (!l[n]) - return -ENOMEM; - - l[++n] = NULL; - } else - n++; - } - - if (list) { - *list = l; - l = NULL; /* avoid freeing */ - } - - return n; -} - -char *strjoin(const char *x, ...) { - va_list ap; - size_t l; - char *r, *p; - - va_start(ap, x); - - if (x) { - l = strlen(x); - - for (;;) { - const char *t; - size_t n; - - t = va_arg(ap, const char *); - if (!t) - break; - - n = strlen(t); - if (n > ((size_t) -1) - l) { - va_end(ap); - return NULL; - } - - l += n; - } - } else - l = 0; - - va_end(ap); - - r = new(char, l+1); - if (!r) - return NULL; - - if (x) { - p = stpcpy(r, x); - - va_start(ap, x); - - for (;;) { - const char *t; - - t = va_arg(ap, const char *); - if (!t) - break; - - p = stpcpy(p, t); - } - - va_end(ap); - } else - r[0] = 0; - - return r; -} - -bool is_main_thread(void) { - static thread_local int cached = 0; - - if (_unlikely_(cached == 0)) - cached = getpid() == gettid() ? 1 : -1; - - return cached > 0; -} - int block_get_whole_disk(dev_t d, dev_t *ret) { char *p, *s; int r; @@ -3654,104 +363,6 @@ int block_get_whole_disk(dev_t d, dev_t *ret) { return -ENOENT; } -static const char *const ioprio_class_table[] = { - [IOPRIO_CLASS_NONE] = "none", - [IOPRIO_CLASS_RT] = "realtime", - [IOPRIO_CLASS_BE] = "best-effort", - [IOPRIO_CLASS_IDLE] = "idle" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX); - -static const char *const sigchld_code_table[] = { - [CLD_EXITED] = "exited", - [CLD_KILLED] = "killed", - [CLD_DUMPED] = "dumped", - [CLD_TRAPPED] = "trapped", - [CLD_STOPPED] = "stopped", - [CLD_CONTINUED] = "continued", -}; - -DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int); - -static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = { - [LOG_FAC(LOG_KERN)] = "kern", - [LOG_FAC(LOG_USER)] = "user", - [LOG_FAC(LOG_MAIL)] = "mail", - [LOG_FAC(LOG_DAEMON)] = "daemon", - [LOG_FAC(LOG_AUTH)] = "auth", - [LOG_FAC(LOG_SYSLOG)] = "syslog", - [LOG_FAC(LOG_LPR)] = "lpr", - [LOG_FAC(LOG_NEWS)] = "news", - [LOG_FAC(LOG_UUCP)] = "uucp", - [LOG_FAC(LOG_CRON)] = "cron", - [LOG_FAC(LOG_AUTHPRIV)] = "authpriv", - [LOG_FAC(LOG_FTP)] = "ftp", - [LOG_FAC(LOG_LOCAL0)] = "local0", - [LOG_FAC(LOG_LOCAL1)] = "local1", - [LOG_FAC(LOG_LOCAL2)] = "local2", - [LOG_FAC(LOG_LOCAL3)] = "local3", - [LOG_FAC(LOG_LOCAL4)] = "local4", - [LOG_FAC(LOG_LOCAL5)] = "local5", - [LOG_FAC(LOG_LOCAL6)] = "local6", - [LOG_FAC(LOG_LOCAL7)] = "local7" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0)); - -static const char *const log_level_table[] = { - [LOG_EMERG] = "emerg", - [LOG_ALERT] = "alert", - [LOG_CRIT] = "crit", - [LOG_ERR] = "err", - [LOG_WARNING] = "warning", - [LOG_NOTICE] = "notice", - [LOG_INFO] = "info", - [LOG_DEBUG] = "debug" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG); - -static const char* const sched_policy_table[] = { - [SCHED_OTHER] = "other", - [SCHED_BATCH] = "batch", - [SCHED_IDLE] = "idle", - [SCHED_FIFO] = "fifo", - [SCHED_RR] = "rr" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX); - -static const char* const rlimit_table[_RLIMIT_MAX] = { - [RLIMIT_CPU] = "LimitCPU", - [RLIMIT_FSIZE] = "LimitFSIZE", - [RLIMIT_DATA] = "LimitDATA", - [RLIMIT_STACK] = "LimitSTACK", - [RLIMIT_CORE] = "LimitCORE", - [RLIMIT_RSS] = "LimitRSS", - [RLIMIT_NOFILE] = "LimitNOFILE", - [RLIMIT_AS] = "LimitAS", - [RLIMIT_NPROC] = "LimitNPROC", - [RLIMIT_MEMLOCK] = "LimitMEMLOCK", - [RLIMIT_LOCKS] = "LimitLOCKS", - [RLIMIT_SIGPENDING] = "LimitSIGPENDING", - [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE", - [RLIMIT_NICE] = "LimitNICE", - [RLIMIT_RTPRIO] = "LimitRTPRIO", - [RLIMIT_RTTIME] = "LimitRTTIME" -}; - -DEFINE_STRING_TABLE_LOOKUP(rlimit, int); - -static const char* const ip_tos_table[] = { - [IPTOS_LOWDELAY] = "low-delay", - [IPTOS_THROUGHPUT] = "throughput", - [IPTOS_RELIABILITY] = "reliability", - [IPTOS_LOWCOST] = "low-cost", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); - bool kexec_loaded(void) { bool loaded = false; char *s; @@ -3782,93 +393,6 @@ int prot_from_flags(int flags) { } } -char *format_bytes(char *buf, size_t l, uint64_t t) { - unsigned i; - - 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; - -} - -void* memdup(const void *p, size_t l) { - void *r; - - assert(p); - - r = malloc(l); - if (!r) - return NULL; - - memcpy(r, p, l); - return r; -} - -int fd_inc_sndbuf(int fd, size_t n) { - int r, value; - socklen_t l = sizeof(value); - - r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) - return 0; - - /* If we have the privileges we will ignore the kernel limit. */ - - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) - return -errno; - - return 1; -} - -int fd_inc_rcvbuf(int fd, size_t n) { - int r, value; - socklen_t l = sizeof(value); - - r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) - return 0; - - /* If we have the privileges we will ignore the kernel limit. */ - - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) - return -errno; - return 1; -} - int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) { bool stdout_is_tty, stderr_is_tty; pid_t parent_pid, agent_pid; @@ -3971,82 +495,6 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa _exit(EXIT_FAILURE); } -int setrlimit_closest(int resource, const struct rlimit *rlim) { - struct rlimit highest, fixed; - - assert(rlim); - - if (setrlimit(resource, rlim) >= 0) - return 0; - - if (errno != EPERM) - return -errno; - - /* So we failed to set the desired setrlimit, then let's try - * to get as close as we can */ - assert_se(getrlimit(resource, &highest) == 0); - - fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max); - fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max); - - if (setrlimit(resource, &fixed) < 0) - return -errno; - - return 0; -} - -bool http_etag_is_valid(const char *etag) { - if (isempty(etag)) - return false; - - if (!endswith(etag, "\"")) - return false; - - if (!startswith(etag, "\"") && !startswith(etag, "W/\"")) - return false; - - return true; -} - -bool http_url_is_valid(const char *url) { - const char *p; - - if (isempty(url)) - return false; - - p = startswith(url, "http://"); - if (!p) - p = startswith(url, "https://"); - if (!p) - return false; - - if (isempty(p)) - return false; - - return ascii_is_valid(p); -} - -bool documentation_url_is_valid(const char *url) { - const char *p; - - if (isempty(url)) - return false; - - if (http_url_is_valid(url)) - return true; - - p = startswith(url, "file:/"); - if (!p) - p = startswith(url, "info:"); - if (!p) - p = startswith(url, "man:"); - - if (isempty(p)) - return false; - - return ascii_is_valid(p); -} - bool in_initrd(void) { static int saved = -1; struct statfs s; @@ -4071,181 +519,6 @@ bool in_initrd(void) { return saved; } -int get_home_dir(char **_h) { - struct passwd *p; - const char *e; - char *h; - uid_t u; - - assert(_h); - - /* Take the user specified one */ - e = secure_getenv("HOME"); - if (e && path_is_absolute(e)) { - h = strdup(e); - if (!h) - return -ENOMEM; - - *_h = h; - return 0; - } - - /* Hardcode home directory for root to avoid NSS */ - u = getuid(); - if (u == 0) { - h = strdup("/root"); - if (!h) - return -ENOMEM; - - *_h = h; - return 0; - } - - /* Check the database... */ - errno = 0; - p = getpwuid(u); - if (!p) - return errno > 0 ? -errno : -ESRCH; - - if (!path_is_absolute(p->pw_dir)) - return -EINVAL; - - h = strdup(p->pw_dir); - if (!h) - return -ENOMEM; - - *_h = h; - return 0; -} - -int get_shell(char **_s) { - struct passwd *p; - const char *e; - char *s; - uid_t u; - - assert(_s); - - /* Take the user specified one */ - e = getenv("SHELL"); - if (e) { - s = strdup(e); - if (!s) - return -ENOMEM; - - *_s = s; - return 0; - } - - /* Hardcode home directory for root to avoid NSS */ - u = getuid(); - if (u == 0) { - s = strdup("/bin/sh"); - if (!s) - return -ENOMEM; - - *_s = s; - return 0; - } - - /* Check the database... */ - errno = 0; - p = getpwuid(u); - if (!p) - return errno > 0 ? -errno : -ESRCH; - - if (!path_is_absolute(p->pw_shell)) - return -EINVAL; - - s = strdup(p->pw_shell); - if (!s) - return -ENOMEM; - - *_s = s; - return 0; -} - -bool filename_is_valid(const char *p) { - - if (isempty(p)) - return false; - - if (strchr(p, '/')) - return false; - - if (streq(p, ".")) - return false; - - if (streq(p, "..")) - return false; - - if (strlen(p) > FILENAME_MAX) - return false; - - return true; -} - -bool string_is_safe(const char *p) { - const char *t; - - if (!p) - return false; - - for (t = p; *t; t++) { - if (*t > 0 && *t < ' ') - return false; - - if (strchr("\\\"\'\x7f", *t)) - return false; - } - - return true; -} - -/** - * Check if a string contains control characters. If 'ok' is non-NULL - * it may be a string containing additional CCs to be considered OK. - */ -bool string_has_cc(const char *p, const char *ok) { - const char *t; - - assert(p); - - for (t = p; *t; t++) { - if (ok && strchr(ok, *t)) - continue; - - if (*t > 0 && *t < ' ') - return true; - - if (*t == 127) - return true; - } - - return false; -} - -bool path_is_safe(const char *p) { - - if (isempty(p)) - return false; - - if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) - return false; - - if (strlen(p)+1 > PATH_MAX) - return false; - - /* The following two checks are not really dangerous, but hey, they still are confusing */ - if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) - return false; - - if (strstr(p, "//")) - return false; - - return true; -} - /* 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, int (*compar) (const void *, const void *, void *), void *arg) { @@ -4269,216 +542,6 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, return NULL; } -void init_gettext(void) { - setlocale(LC_ALL, ""); - textdomain(GETTEXT_PACKAGE); -} - -bool is_locale_utf8(void) { - const char *set; - static int cached_answer = -1; - - if (cached_answer >= 0) - goto out; - - if (!setlocale(LC_ALL, "")) { - cached_answer = true; - goto out; - } - - set = nl_langinfo(CODESET); - if (!set) { - cached_answer = true; - goto out; - } - - if (streq(set, "UTF-8")) { - cached_answer = true; - goto out; - } - - /* For LC_CTYPE=="C" return true, because CTYPE is effectly - * unset and everything can do to UTF-8 nowadays. */ - set = setlocale(LC_CTYPE, NULL); - if (!set) { - cached_answer = true; - goto out; - } - - /* Check result, but ignore the result if C was set - * explicitly. */ - cached_answer = - STR_IN_SET(set, "C", "POSIX") && - !getenv("LC_ALL") && - !getenv("LC_CTYPE") && - !getenv("LANG"); - -out: - return (bool) cached_answer; -} - -const char *draw_special_char(DrawSpecialChar ch) { - static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = { - - /* UTF-8 */ { - [DRAW_TREE_VERTICAL] = "\342\224\202 ", /* │ */ - [DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ - [DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ - [DRAW_TREE_SPACE] = " ", /* */ - [DRAW_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ - [DRAW_BLACK_CIRCLE] = "\342\227\217", /* ● */ - [DRAW_ARROW] = "\342\206\222", /* → */ - [DRAW_DASH] = "\342\200\223", /* – */ - }, - - /* ASCII fallback */ { - [DRAW_TREE_VERTICAL] = "| ", - [DRAW_TREE_BRANCH] = "|-", - [DRAW_TREE_RIGHT] = "`-", - [DRAW_TREE_SPACE] = " ", - [DRAW_TRIANGULAR_BULLET] = ">", - [DRAW_BLACK_CIRCLE] = "*", - [DRAW_ARROW] = "->", - [DRAW_DASH] = "-", - } - }; - - return draw_table[!is_locale_utf8()][ch]; -} - -char *strreplace(const char *text, const char *old_string, const char *new_string) { - const char *f; - char *t, *r; - size_t l, old_len, new_len; - - assert(text); - assert(old_string); - assert(new_string); - - old_len = strlen(old_string); - new_len = strlen(new_string); - - l = strlen(text); - r = new(char, l+1); - if (!r) - return NULL; - - f = text; - t = r; - while (*f) { - char *a; - size_t d, nl; - - if (!startswith(f, old_string)) { - *(t++) = *(f++); - continue; - } - - d = t - r; - nl = l - old_len + new_len; - a = realloc(r, nl + 1); - if (!a) - goto oom; - - l = nl; - r = a; - t = r + d; - - t = stpcpy(t, new_string); - f += old_len; - } - - *t = 0; - return r; - -oom: - free(r); - return NULL; -} - -char *strip_tab_ansi(char **ibuf, size_t *_isz) { - const char *i, *begin = NULL; - enum { - STATE_OTHER, - STATE_ESCAPE, - STATE_BRACKET - } state = STATE_OTHER; - char *obuf = NULL; - size_t osz = 0, isz; - FILE *f; - - assert(ibuf); - assert(*ibuf); - - /* Strips ANSI color and replaces TABs by 8 spaces */ - - isz = _isz ? *_isz : strlen(*ibuf); - - f = open_memstream(&obuf, &osz); - if (!f) - return NULL; - - for (i = *ibuf; i < *ibuf + isz + 1; i++) { - - switch (state) { - - case STATE_OTHER: - if (i >= *ibuf + isz) /* EOT */ - break; - else if (*i == '\x1B') - state = STATE_ESCAPE; - else if (*i == '\t') - fputs(" ", f); - else - fputc(*i, f); - break; - - case STATE_ESCAPE: - if (i >= *ibuf + isz) { /* EOT */ - fputc('\x1B', f); - break; - } else if (*i == '[') { - state = STATE_BRACKET; - begin = i + 1; - } else { - fputc('\x1B', f); - fputc(*i, f); - state = STATE_OTHER; - } - - break; - - case STATE_BRACKET: - - if (i >= *ibuf + isz || /* EOT */ - (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) { - fputc('\x1B', f); - fputc('[', f); - state = STATE_OTHER; - i = begin-1; - } else if (*i == 'm') - state = STATE_OTHER; - break; - } - } - - if (ferror(f)) { - fclose(f); - free(obuf); - return NULL; - } - - fclose(f); - - free(*ibuf); - *ibuf = obuf; - - if (_isz) - *_isz = osz; - - return obuf; -} - int on_ac_power(void) { bool found_offline = false, found_online = false; _cleanup_closedir_ DIR *d = NULL; @@ -4555,204 +618,6 @@ int on_ac_power(void) { return found_online || !found_offline; } -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, NULL); - else - p = strjoin(*i, "/", path, NULL); - 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); -} - -char *strextend(char **x, ...) { - va_list ap; - size_t f, l; - char *r, *p; - - assert(x); - - l = f = *x ? strlen(*x) : 0; - - va_start(ap, x); - for (;;) { - const char *t; - size_t n; - - t = va_arg(ap, const char *); - if (!t) - break; - - n = strlen(t); - if (n > ((size_t) -1) - l) { - va_end(ap); - return NULL; - } - - l += n; - } - va_end(ap); - - r = realloc(*x, l+1); - if (!r) - return NULL; - - p = r + f; - - va_start(ap, x); - for (;;) { - const char *t; - - t = va_arg(ap, const char *); - if (!t) - break; - - p = stpcpy(p, t); - } - va_end(ap); - - *p = 0; - *x = r; - - return r + l; -} - -char *strrep(const char *s, unsigned n) { - size_t l; - char *r, *p; - unsigned i; - - assert(s); - - l = strlen(s); - p = r = malloc(l * n + 1); - if (!r) - return NULL; - - for (i = 0; i < n; i++) - p = stpcpy(p, s); - - *p = 0; - return r; -} - -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; -} - bool id128_is_valid(const char *s) { size_t i, l; @@ -4794,151 +659,6 @@ bool id128_is_valid(const char *s) { return true; } -int split_pair(const char *s, const char *sep, char **l, char **r) { - char *x, *a, *b; - - assert(s); - assert(sep); - assert(l); - assert(r); - - if (isempty(sep)) - return -EINVAL; - - x = strstr(s, sep); - if (!x) - return -EINVAL; - - a = strndup(s, x - s); - if (!a) - return -ENOMEM; - - b = strdup(x + strlen(sep)); - if (!b) { - free(a); - return -ENOMEM; - } - - *l = a; - *r = b; - - return 0; -} - -int shall_restore_state(void) { - _cleanup_free_ char *value = NULL; - int r; - - r = get_proc_cmdline_key("systemd.restore_state=", &value); - if (r < 0) - return r; - if (r == 0) - return true; - - return parse_boolean(value) != 0; -} - -int proc_cmdline(char **ret) { - assert(ret); - - if (detect_container() > 0) - return get_process_cmdline(1, 0, false, ret); - else - return read_one_line_file("/proc/cmdline", ret); -} - -int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) { - _cleanup_free_ char *line = NULL; - const char *p; - int r; - - assert(parse_item); - - r = proc_cmdline(&line); - if (r < 0) - return r; - - p = line; - for (;;) { - _cleanup_free_ char *word = NULL; - char *value = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); - if (r < 0) - return r; - if (r == 0) - break; - - /* Filter out arguments that are intended only for the - * initrd */ - if (!in_initrd() && startswith(word, "rd.")) - continue; - - value = strchr(word, '='); - if (value) - *(value++) = 0; - - r = parse_item(word, value); - if (r < 0) - return r; - } - - return 0; -} - -int get_proc_cmdline_key(const char *key, char **value) { - _cleanup_free_ char *line = NULL, *ret = NULL; - bool found = false; - const char *p; - int r; - - assert(key); - - r = proc_cmdline(&line); - if (r < 0) - return r; - - p = line; - for (;;) { - _cleanup_free_ char *word = NULL; - const char *e; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); - if (r < 0) - return r; - if (r == 0) - break; - - /* Filter out arguments that are intended only for the - * initrd */ - if (!in_initrd() && startswith(word, "rd.")) - continue; - - if (value) { - e = startswith(word, key); - if (!e) - continue; - - r = free_and_strdup(&ret, e); - if (r < 0) - return r; - - found = true; - } else { - if (streq(word, key)) - found = true; - } - } - - if (value) { - *value = ret; - ret = NULL; - } - - return found; - -} - int container_get_leader(const char *machine, pid_t *pid) { _cleanup_free_ char *s = NULL, *class = NULL; const char *p; @@ -5088,203 +808,6 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int return reset_uid_gid(); } -int getpeercred(int fd, struct ucred *ucred) { - socklen_t n = sizeof(struct ucred); - struct ucred u; - int r; - - assert(fd >= 0); - assert(ucred); - - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n); - if (r < 0) - return -errno; - - if (n != sizeof(struct ucred)) - return -EIO; - - /* Check if the data is actually useful and not suppressed due - * to namespacing issues */ - if (u.pid <= 0) - return -ENODATA; - if (u.uid == UID_INVALID) - return -ENODATA; - if (u.gid == GID_INVALID) - return -ENODATA; - - *ucred = u; - return 0; -} - -int getpeersec(int fd, char **ret) { - socklen_t n = 64; - char *s; - int r; - - assert(fd >= 0); - assert(ret); - - s = new0(char, n); - if (!s) - return -ENOMEM; - - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); - - if (errno != ERANGE) - return -errno; - - s = new0(char, n); - if (!s) - return -ENOMEM; - - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); - return -errno; - } - } - - if (isempty(s)) { - free(s); - return -EOPNOTSUPP; - } - - *ret = s; - return 0; -} - -/* This is much like like mkostemp() but is subject to umask(). */ -int mkostemp_safe(char *pattern, int flags) { - _cleanup_umask_ mode_t u; - int fd; - - assert(pattern); - - u = umask(077); - - fd = mkostemp(pattern, flags); - if (fd < 0) - return -errno; - - return fd; -} - -int open_tmpfile(const char *path, int flags) { - char *p; - int fd; - - assert(path); - -#ifdef O_TMPFILE - /* Try O_TMPFILE first, if it is supported */ - fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); - if (fd >= 0) - return fd; -#endif - - /* Fall back to unguessable name + unlinking */ - p = strjoina(path, "/systemd-tmp-XXXXXX"); - - fd = mkostemp_safe(p, flags); - if (fd < 0) - return fd; - - unlink(p); - return fd; -} - -int fd_warn_permissions(const char *path, int fd) { - struct stat st; - - if (fstat(fd, &st) < 0) - return -errno; - - if (st.st_mode & 0111) - log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); - - if (st.st_mode & 0002) - log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); - - if (getpid() == 1 && (st.st_mode & 0044) != 0044) - log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); - - return 0; -} - -unsigned long personality_from_string(const char *p) { - - /* Parse a personality specifier. We introduce our own - * identifiers that indicate specific ABIs, rather than just - * hints regarding the register size, since we want to keep - * things open for multiple locally supported ABIs for the - * same register size. We try to reuse the ABI identifiers - * used by libseccomp. */ - -#if defined(__x86_64__) - - if (streq(p, "x86")) - return PER_LINUX32; - - if (streq(p, "x86-64")) - return PER_LINUX; - -#elif defined(__i386__) - - if (streq(p, "x86")) - return PER_LINUX; - -#elif defined(__s390x__) - - if (streq(p, "s390")) - return PER_LINUX32; - - if (streq(p, "s390x")) - return PER_LINUX; - -#elif defined(__s390__) - - if (streq(p, "s390")) - return PER_LINUX; -#endif - - return PERSONALITY_INVALID; -} - -const char* personality_to_string(unsigned long p) { - -#if defined(__x86_64__) - - if (p == PER_LINUX32) - return "x86"; - - if (p == PER_LINUX) - return "x86-64"; - -#elif defined(__i386__) - - if (p == PER_LINUX) - return "x86"; - -#elif defined(__s390x__) - - if (p == PER_LINUX) - return "s390x"; - - if (p == PER_LINUX32) - return "s390"; - -#elif defined(__s390__) - - if (p == PER_LINUX) - return "s390"; - -#endif - - return NULL; -} - uint64_t physical_memory(void) { long mem; @@ -5297,49 +820,6 @@ uint64_t physical_memory(void) { return (uint64_t) mem * (uint64_t) page_size(); } -void hexdump(FILE *f, const void *p, size_t s) { - const uint8_t *b = p; - unsigned n = 0; - - assert(s == 0 || b); - - 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; - } -} - int update_reboot_param_file(const char *param) { int r = 0; @@ -5353,1522 +833,8 @@ int update_reboot_param_file(const char *param) { return 0; } -int umount_recursive(const char *prefix, int flags) { - bool again; - int n = 0, r; - - /* Try to umount everything recursively below a - * directory. Also, take care of stacked mounts, and keep - * unmounting them until they are gone. */ - - do { - _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; - - again = false; - r = 0; - - proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!proc_self_mountinfo) - return -errno; - - for (;;) { - _cleanup_free_ char *path = NULL, *p = NULL; - int k; - - k = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount options */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%*s " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%*s" /* (11) mount options 2 */ - "%*[^\n]", /* some rubbish at the end */ - &path); - if (k != 1) { - if (k == EOF) - break; - - continue; - } - - r = cunescape(path, UNESCAPE_RELAX, &p); - if (r < 0) - return r; - - if (!path_startswith(p, prefix)) - continue; - - if (umount2(p, flags) < 0) { - r = -errno; - continue; - } - - again = true; - n++; - - break; - } - - } while (again); - - return r ? r : n; -} - -static int get_mount_flags(const char *path, unsigned long *flags) { - struct statvfs buf; - - if (statvfs(path, &buf) < 0) - return -errno; - *flags = buf.f_flag; - return 0; -} - -int bind_remount_recursive(const char *prefix, bool ro) { - _cleanup_set_free_free_ Set *done = NULL; - _cleanup_free_ char *cleaned = NULL; - int r; - - /* Recursively remount a directory (and all its submounts) - * read-only or read-write. If the directory is already - * mounted, we reuse the mount and simply mark it - * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write - * operation). If it isn't we first make it one. Afterwards we - * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all - * submounts we can access, too. When mounts are stacked on - * the same mount point we only care for each individual - * "top-level" mount on each point, as we cannot - * influence/access the underlying mounts anyway. We do not - * have any effect on future submounts that might get - * propagated, they migt be writable. This includes future - * submounts that have been triggered via autofs. */ - - cleaned = strdup(prefix); - if (!cleaned) - return -ENOMEM; - - path_kill_slashes(cleaned); - - done = set_new(&string_hash_ops); - if (!done) - return -ENOMEM; - - for (;;) { - _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; - _cleanup_set_free_free_ Set *todo = NULL; - bool top_autofs = false; - char *x; - unsigned long orig_flags; - - todo = set_new(&string_hash_ops); - if (!todo) - return -ENOMEM; - - proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!proc_self_mountinfo) - return -errno; - - for (;;) { - _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL; - int k; - - k = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount options (superblock) */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%ms " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%*s" /* (11) mount options (bind mount) */ - "%*[^\n]", /* some rubbish at the end */ - &path, - &type); - if (k != 2) { - if (k == EOF) - break; - - continue; - } - - r = cunescape(path, UNESCAPE_RELAX, &p); - if (r < 0) - return r; - - /* Let's ignore autofs mounts. If they aren't - * triggered yet, we want to avoid triggering - * them, as we don't make any guarantees for - * future submounts anyway. If they are - * already triggered, then we will find - * another entry for this. */ - if (streq(type, "autofs")) { - top_autofs = top_autofs || path_equal(cleaned, p); - continue; - } - - if (path_startswith(p, cleaned) && - !set_contains(done, p)) { - - r = set_consume(todo, p); - p = NULL; - - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } - - /* If we have no submounts to process anymore and if - * the root is either already done, or an autofs, we - * are done */ - if (set_isempty(todo) && - (top_autofs || set_contains(done, cleaned))) - return 0; - - if (!set_contains(done, cleaned) && - !set_contains(todo, cleaned)) { - /* The prefix directory itself is not yet a - * mount, make it one. */ - if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0) - return -errno; - - orig_flags = 0; - (void) get_mount_flags(cleaned, &orig_flags); - orig_flags &= ~MS_RDONLY; - - if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) - return -errno; - - x = strdup(cleaned); - if (!x) - return -ENOMEM; - - r = set_consume(done, x); - if (r < 0) - return r; - } - - while ((x = set_steal_first(todo))) { - - r = set_consume(done, x); - if (r == -EEXIST || r == 0) - continue; - if (r < 0) - return r; - - /* Try to reuse the original flag set, but - * don't care for errors, in case of - * obstructed mounts */ - orig_flags = 0; - (void) get_mount_flags(x, &orig_flags); - orig_flags &= ~MS_RDONLY; - - if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) { - - /* Deal with mount points that are - * obstructed by a later mount */ - - if (errno != ENOENT) - return -errno; - } - - } - } -} - -int fflush_and_check(FILE *f) { - assert(f); - - errno = 0; - fflush(f); - - if (ferror(f)) - return errno ? -errno : -EIO; - - return 0; -} - -int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { - const char *fn; - char *t; - - assert(p); - assert(ret); - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldoXXXXXX - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - if (extra == NULL) - 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_kill_slashes(t); - return 0; -} - -int tempfn_random(const char *p, const char *extra, char **ret) { - const char *fn; - char *t, *x; - uint64_t u; - unsigned i; - - assert(p); - assert(ret); - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldobaa2a261115984a9 - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - if (!extra) - 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_kill_slashes(t); - return 0; -} - -int tempfn_random_child(const char *p, const char *extra, char **ret) { - char *t, *x; - uint64_t u; - unsigned i; - - assert(p); - assert(ret); - - /* Turns this: - * /foo/bar/waldo - * Into this: - * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0 - */ - - if (!extra) - extra = ""; - - t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); - if (!t) - return -ENOMEM; - - 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_kill_slashes(t); - return 0; -} - -int take_password_lock(const char *root) { - - struct flock flock = { - .l_type = F_WRLCK, - .l_whence = SEEK_SET, - .l_start = 0, - .l_len = 0, - }; - - const char *path; - int fd, r; - - /* This is roughly the same as lckpwdf(), but not as awful. We - * don't want to use alarm() and signals, hence we implement - * our own trivial version of this. - * - * Note that shadow-utils also takes per-database locks in - * addition to lckpwdf(). However, we don't given that they - * are redundant as they they invoke lckpwdf() first and keep - * it during everything they do. The per-database locks are - * awfully racy, and thus we just won't do them. */ - - if (root) - path = strjoina(root, "/etc/.pwd.lock"); - else - path = "/etc/.pwd.lock"; - - fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600); - if (fd < 0) - return -errno; - - r = fcntl(fd, F_SETLKW, &flock); - if (r < 0) { - safe_close(fd); - return -errno; - } - - return fd; -} - -int is_symlink(const char *path) { - struct stat info; - - 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; - - if (follow) - r = stat(path, &st); - else - r = lstat(path, &st); - if (r < 0) - return -errno; - - return !!S_ISDIR(st.st_mode); -} - -int is_device_node(const char *path) { - struct stat info; - - if (lstat(path, &info) < 0) - return -errno; - - return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); -} - -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; - int r; - - enum { - START, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE, - SINGLE_QUOTE_ESCAPE, - DOUBLE_QUOTE, - DOUBLE_QUOTE_ESCAPE, - SEPARATOR, - } state = START; - - assert(p); - assert(ret); - - if (!separators) - separators = WHITESPACE; - - /* Bail early if called after last value or with no input */ - if (!*p) - goto finish_force_terminate; - - /* 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. */ - - for (;;) { - char c = **p; - - switch (state) { - - case START: - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - - if (c == 0) - goto finish_force_terminate; - else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p) ++; - goto finish_force_next; - } - break; - } - - /* 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; - - state = VALUE; - /* fallthrough */ - - case VALUE: - if (c == 0) - goto finish_force_terminate; - else if (c == '\'' && (flags & EXTRACT_QUOTES)) - state = SINGLE_QUOTE; - else if (c == '\\') - state = VALUE_ESCAPE; - else if (c == '\"' && (flags & EXTRACT_QUOTES)) - state = DOUBLE_QUOTE; - else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p) ++; - goto finish_force_next; - } - state = SEPARATOR; - } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - - break; - - case SINGLE_QUOTE: - if (c == 0) { - if (flags & EXTRACT_RELAX) - goto finish_force_terminate; - return -EINVAL; - } else if (c == '\'') - state = VALUE; - else if (c == '\\') - state = SINGLE_QUOTE_ESCAPE; - else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - - break; - - case DOUBLE_QUOTE: - if (c == 0) - return -EINVAL; - else if (c == '\"') - state = VALUE; - else if (c == '\\') - state = DOUBLE_QUOTE_ESCAPE; - else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - - break; - - case SINGLE_QUOTE_ESCAPE: - case DOUBLE_QUOTE_ESCAPE: - case VALUE_ESCAPE: - if (!GREEDY_REALLOC(s, allocated, sz+7)) - return -ENOMEM; - - if (c == 0) { - if ((flags & EXTRACT_CUNESCAPE_RELAX) && - (state == VALUE_ESCAPE || 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) { - uint32_t u; - - r = cunescape_one(*p, (size_t) -1, &c, &u); - if (r < 0) { - if (flags & EXTRACT_CUNESCAPE_RELAX) { - s[sz++] = '\\'; - s[sz++] = c; - goto end_escape; - } - return -EINVAL; - } - - (*p) += r - 1; - - if (c != 0) - s[sz++] = c; /* normal explicit char */ - else - sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */ - } else - s[sz++] = c; - -end_escape: - state = (state == SINGLE_QUOTE_ESCAPE) ? SINGLE_QUOTE : - (state == DOUBLE_QUOTE_ESCAPE) ? DOUBLE_QUOTE : - VALUE; - break; - - case SEPARATOR: - if (c == 0) - goto finish_force_terminate; - if (!strchr(separators, c)) - goto finish; - break; - } - - (*p) ++; - } - -finish_force_terminate: - *p = NULL; -finish: - if (!s) { - *p = NULL; - *ret = NULL; - return 0; - } - -finish_force_next: - s[sz] = 0; - *ret = s; - s = NULL; - - return 1; -} - -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 && !(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) - log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue); - else - log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid escape sequences in command line: \"%s\"", rvalue); - } - - return r; -} - -int extract_many_words(const char **p, const char *separators, ExtractFlags 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; -} - -int free_and_strdup(char **p, const char *s) { - char *t; - - assert(p); - - /* Replaces a string pointer with an strdup()ed new string, - * possibly freeing the old one. */ - - if (streq_ptr(*p, s)) - return 0; - - if (s) { - t = strdup(s); - if (!t) - return -ENOMEM; - } else - t = NULL; - - free(*p); - *p = t; - - return 1; -} - -int ptsname_malloc(int fd, char **ret) { - size_t l = 100; - - assert(fd >= 0); - assert(ret); - - for (;;) { - char *c; - - c = new(char, l); - if (!c) - return -ENOMEM; - - if (ptsname_r(fd, c, l) == 0) { - *ret = c; - return 0; - } - if (errno != ERANGE) { - free(c); - return -errno; - } - - free(c); - l *= 2; - } -} - -int openpt_in_namespace(pid_t pid, int flags) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - siginfo_t si; - pid_t child; - int r; - - assert(pid > 0); - - r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); - if (r < 0) - return r; - - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { - int master; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd); - if (r < 0) - _exit(EXIT_FAILURE); - - master = posix_openpt(flags); - if (master < 0) - _exit(EXIT_FAILURE); - - if (unlockpt(master) < 0) - _exit(EXIT_FAILURE); - - if (send_one_fd(pair[1], master, 0) < 0) - _exit(EXIT_FAILURE); - - _exit(EXIT_SUCCESS); - } - - pair[1] = safe_close(pair[1]); - - r = wait_for_terminate(child, &si); - if (r < 0) - return r; - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return -EIO; - - return receive_one_fd(pair[0], 0); -} - -ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) { - char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - _cleanup_close_ int fd = -1; - ssize_t l; - - /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */ - - fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0)); - if (fd < 0) - return -errno; - - xsprintf(fn, "/proc/self/fd/%i", fd); - - l = getxattr(fn, attribute, value, size); - if (l < 0) - return -errno; - - return l; -} - -static int parse_crtime(le64_t le, usec_t *usec) { - uint64_t u; - - assert(usec); - - u = le64toh(le); - if (u == 0 || u == (uint64_t) -1) - return -EIO; - - *usec = (usec_t) u; - return 0; -} - -int fd_getcrtime(int fd, usec_t *usec) { - le64_t le; - ssize_t n; - - assert(fd >= 0); - assert(usec); - - /* Until Linux gets a real concept of birthtime/creation time, - * let's fake one with xattrs */ - - n = fgetxattr(fd, "user.crtime_usec", &le, sizeof(le)); - if (n < 0) - return -errno; - if (n != sizeof(le)) - return -EIO; - - return parse_crtime(le, usec); -} - -int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) { - le64_t le; - ssize_t n; - - n = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags); - if (n < 0) - return -errno; - if (n != sizeof(le)) - return -EIO; - - return parse_crtime(le, usec); -} - -int path_getcrtime(const char *p, usec_t *usec) { - le64_t le; - ssize_t n; - - assert(p); - assert(usec); - - n = getxattr(p, "user.crtime_usec", &le, sizeof(le)); - if (n < 0) - return -errno; - if (n != sizeof(le)) - return -EIO; - - return parse_crtime(le, usec); -} - -int fd_setcrtime(int fd, usec_t usec) { - le64_t le; - - assert(fd >= 0); - - if (usec <= 0) - usec = now(CLOCK_REALTIME); - - le = htole64((uint64_t) usec); - if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0) - return -errno; - - return 0; -} - -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(); - r = kcmp(pid, pid, KCMP_FILE, a, b); - if (r == 0) - return true; - if (r > 0) - return false; - if (errno != ENOSYS) - 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; -} - -int chattr_fd(int fd, unsigned value, unsigned mask) { - unsigned old_attr, new_attr; - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - /* Explicitly check whether this is a regular file or - * directory. If it is anything else (such as a device node or - * fifo), then the ioctl will not hit the file systems but - * possibly drivers, where the ioctl might have different - * effects. Notably, DRM is using the same ioctl() number. */ - - if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) - return -ENOTTY; - - if (mask == 0) - return 0; - - if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0) - return -errno; - - new_attr = (old_attr & ~mask) | (value & mask); - if (new_attr == old_attr) - return 0; - - if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0) - return -errno; - - return 1; -} - -int chattr_path(const char *p, unsigned value, unsigned mask) { - _cleanup_close_ int fd = -1; - - assert(p); - - if (mask == 0) - return 0; - - fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return chattr_fd(fd, value, mask); -} - -int read_attr_fd(int fd, unsigned *ret) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) - return -ENOTTY; - - if (ioctl(fd, FS_IOC_GETFLAGS, ret) < 0) - return -errno; - - return 0; -} - -int read_attr_path(const char *p, unsigned *ret) { - _cleanup_close_ int fd = -1; - - assert(p); - assert(ret); - - fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return read_attr_fd(fd, ret); -} - -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; -} - -void sigkill_wait(pid_t *pid) { - if (!pid) - return; - if (*pid <= 1) - return; - - if (kill(*pid, SIGKILL) > 0) - (void) wait_for_terminate(*pid, NULL); -} - -int syslog_parse_priority(const char **p, int *priority, bool with_facility) { - int a = 0, b = 0, c = 0; - int k; - - assert(p); - assert(*p); - assert(priority); - - if ((*p)[0] != '<') - return 0; - - if (!strchr(*p, '>')) - return 0; - - if ((*p)[2] == '>') { - c = undecchar((*p)[1]); - k = 3; - } else if ((*p)[3] == '>') { - b = undecchar((*p)[1]); - c = undecchar((*p)[2]); - k = 4; - } else if ((*p)[4] == '>') { - a = undecchar((*p)[1]); - b = undecchar((*p)[2]); - c = undecchar((*p)[3]); - k = 5; - } else - return 0; - - if (a < 0 || b < 0 || c < 0 || - (!with_facility && (a || b || c > 7))) - return 0; - - if (with_facility) - *priority = a*100 + b*10 + c; - else - *priority = (*priority & LOG_FACMASK) | c; - - *p += k; - return 1; -} - -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; -} - -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)); -} - -int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - struct stat buf; - int ret; - - ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE); - if (ret >= 0) - return 0; - - /* renameat2() exists since Linux 3.15, btrfs added support for it later. - * If it is not implemented, fallback to another method. */ - if (!IN_SET(errno, EINVAL, ENOSYS)) - return -errno; - - /* The link()/unlink() fallback does not work on directories. But - * renameat() without RENAME_NOREPLACE gives the same semantics on - * directories, except when newpath is an *empty* directory. This is - * good enough. */ - ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW); - if (ret >= 0 && S_ISDIR(buf.st_mode)) { - ret = renameat(olddirfd, oldpath, newdirfd, newpath); - return ret >= 0 ? 0 : -errno; - } - - /* If it is not a directory, use the link()/unlink() fallback. */ - ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0); - if (ret < 0) - return -errno; - - ret = unlinkat(olddirfd, oldpath, 0); - if (ret < 0) { - /* backup errno before the following unlinkat() alters it */ - ret = errno; - (void) unlinkat(newdirfd, newpath, 0); - errno = ret; - return -errno; - } - - return 0; -} - -static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) { - assert(bad); - - for (; *s; s++) { - 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); - *t = 0; - - return r; -} - -char *shell_maybe_quote(const char *s) { - const char *p; - char *r, *t; - - assert(s); - - /* Encloses a string in double quotes if necessary to make it - * OK as shell string. */ - - for (p = s; *p; p++) - if (*p <= ' ' || - *p >= 127 || - strchr(SHELL_NEED_QUOTES, *p)) - break; - - if (!*p) - return strdup(s); - - r = new(char, 1+strlen(s)*2+1+1); - if (!r) - return NULL; - - t = r; - *(t++) = '"'; - t = mempcpy(t, s, p - s); - - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE); - - *(t++)= '"'; - *t = 0; - - return r; -} - -int parse_mode(const char *s, mode_t *ret) { - char *x; - long l; - - assert(s); - assert(ret); - - errno = 0; - l = strtol(s, &x, 8); - if (errno != 0) - return -errno; - - if (!x || x == s || *x) - return -EINVAL; - if (l < 0 || l > 07777) - return -ERANGE; - - *ret = (mode_t) l; - return 0; -} - -int mount_move_root(const char *path) { - assert(path); - - if (chdir(path) < 0) - return -errno; - - if (mount(path, "/", NULL, MS_MOVE, NULL) < 0) - return -errno; - - if (chroot(".") < 0) - return -errno; - - if (chdir("/") < 0) - return -errno; - - return 0; -} - -int reset_uid_gid(void) { - - if (setgroups(0, NULL) < 0) - return -errno; - - if (setresgid(0, 0, 0) < 0) - return -errno; - - if (setresuid(0, 0, 0) < 0) - return -errno; - - return 0; -} - -int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink) { - char *v; - size_t l; - ssize_t n; - - assert(path); - assert(name); - assert(value); - - for (l = 100; ; l = (size_t) n + 1) { - v = new0(char, l); - if (!v) - return -ENOMEM; - - if (allow_symlink) - n = lgetxattr(path, name, v, l); - else - n = getxattr(path, name, v, l); - - if (n >= 0 && (size_t) n < l) { - *value = v; - return n; - } - - free(v); - - if (n < 0 && errno != ERANGE) - return -errno; - - if (allow_symlink) - n = lgetxattr(path, name, NULL, 0); - else - n = getxattr(path, name, NULL, 0); - if (n < 0) - return -errno; - } -} - -int fgetxattr_malloc(int fd, const char *name, char **value) { - char *v; - size_t l; - ssize_t n; - - assert(fd >= 0); - assert(name); - assert(value); - - for (l = 100; ; l = (size_t) n + 1) { - v = new0(char, l); - if (!v) - return -ENOMEM; - - n = fgetxattr(fd, name, v, l); - - if (n >= 0 && (size_t) n < l) { - *value = v; - return n; - } - - free(v); - - if (n < 0 && errno != ERANGE) - return -errno; - - n = fgetxattr(fd, name, NULL, 0); - if (n < 0) - return -errno; - } -} - -int send_one_fd(int transport_fd, int fd, int flags) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg; - - assert(transport_fd >= 0); - assert(fd >= 0); - - cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - - mh.msg_controllen = CMSG_SPACE(sizeof(int)); - if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0) - return -errno; - - return 0; -} - -int receive_one_fd(int transport_fd, int flags) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg, *found = NULL; - - assert(transport_fd >= 0); - - /* - * Receive a single FD via @transport_fd. We don't care for - * the transport-type. We retrieve a single FD at most, so for - * packet-based transports, the caller must ensure to send - * only a single FD per packet. This is best used in - * combination with send_one_fd(). - */ - - if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0) - return -errno; - - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS && - cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - assert(!found); - found = cmsg; - break; - } - } - - if (!found) { - cmsg_close_all(&mh); - return -EIO; - } - - return *(int*) CMSG_DATA(found); -} - -void nop_signal_handler(int sig) { - /* nothing here */ -} - int version(void) { puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES); return 0; } - -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; -} diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h index 034410b8a8..d9d2f72b75 100644 --- a/src/systemd/src/basic/util.h +++ b/src/systemd/src/basic/util.h @@ -22,12 +22,10 @@ ***/ #include <alloca.h> -#include <dirent.h> #include <fcntl.h> #include <inttypes.h> #include <limits.h> #include <locale.h> -#include <mntent.h> #include <stdarg.h> #include <stdbool.h> #include <stddef.h> @@ -46,49 +44,9 @@ #include "missing.h" #include "time-util.h" -/* What is interpreted as whitespace? */ -#define WHITESPACE " \t\n\r" -#define NEWLINE "\n\r" -#define QUOTES "\"\'" -#define COMMENTS "#;" -#define GLOB_CHARS "*?[" - -/* What characters are special in the shell? */ -/* must be escaped outside and inside double-quotes */ -#define SHELL_NEED_ESCAPE "\"\\`$" -/* can be escaped or double-quoted */ -#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;" - -#define FORMAT_BYTES_MAX 8 - size_t page_size(void) _pure_; #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) -#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) - -bool streq_ptr(const char *a, const char *b) _pure_; -int strcmp_ptr(const char *a, const char *b) _pure_; - -#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n))) - -#define new0(t, n) ((t*) calloc((n), sizeof(t))) - -#define newa(t, n) ((t*) alloca(sizeof(t)*(n))) - -#define newa0(t, n) ((t*) alloca0(sizeof(t)*(n))) - -#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) - -#define malloc0(n) (calloc(1, (n))) - -static inline void *mfree(void *memory) { - free(memory); - return NULL; -} - static inline const char* yes_no(bool b) { return b ? "yes" : "no"; } @@ -101,345 +59,13 @@ static inline const char* one_zero(bool b) { return b ? "1" : "0"; } -static inline const char* strempty(const char *s) { - return s ? s : ""; -} - -static inline const char* strnull(const char *s) { - return s ? s : "(null)"; -} - -static inline const char *strna(const char *s) { - return s ? s : "n/a"; -} - -static inline bool isempty(const char *p) { - return !p || !p[0]; -} - -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_; - -int close_nointr(int fd); -int safe_close(int fd); -void safe_close_pair(int p[]); - -void close_many(const int fds[], unsigned n_fd); - -int fclose_nointr(FILE *f); -FILE* safe_fclose(FILE *f); -DIR* safe_closedir(DIR *f); - -int parse_size(const char *t, uint64_t base, uint64_t *size); - -int parse_boolean(const char *v) _pure_; -int parse_pid(const char *s, pid_t* ret_pid); -int parse_uid(const char *s, uid_t* ret_uid); -#define parse_gid(s, ret_gid) parse_uid(s, ret_gid) - -bool uid_is_valid(uid_t uid); - -static inline bool gid_is_valid(gid_t gid) { - return uid_is_valid((uid_t) gid); -} - -int safe_atou(const char *s, unsigned *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_atod(const char *s, double *ret_d); - -int safe_atou8(const char *s, uint8_t *ret); - -#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 - -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); -} - -int safe_atou16(const char *s, uint16_t *ret); -int safe_atoi16(const char *s, int16_t *ret); - -const char* split(const char **state, size_t *l, const char *separator, bool quoted); - -#define FOREACH_WORD(word, length, s, state) \ - _FOREACH_WORD(word, length, s, WHITESPACE, false, state) - -#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ - _FOREACH_WORD(word, length, s, separator, false, state) - -#define FOREACH_WORD_QUOTED(word, length, s, state) \ - _FOREACH_WORD(word, length, s, WHITESPACE, true, state) - -#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ - for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) - -char *strappend(const char *s, const char *suffix); -char *strnappend(const char *s, const char *suffix, size_t length); - -int readlinkat_malloc(int fd, const char *p, char **ret); -int readlink_malloc(const char *p, char **r); -int readlink_value(const char *p, char **ret); -int readlink_and_make_absolute(const char *p, char **r); -int readlink_and_canonicalize(const char *p, char **r); - -char *strstrip(char *s); -char *delete_chars(char *s, const char *bad); -char *truncate_nl(char *s); - -char *file_in_same_dir(const char *path, const char *filename); - -int rmdir_parents(const char *path, const char *stop); - -char hexchar(int x) _const_; -int unhexchar(char c) _const_; -char octchar(int x) _const_; -int unoctchar(char c) _const_; -char decchar(int x) _const_; -int undecchar(char c) _const_; -char base32hexchar(int x) _const_; -int unbase32hexchar(char c) _const_; -char base64char(int x) _const_; -int unbase64char(char c) _const_; - -char *cescape(const char *s); -size_t cescape_char(char c, char *buf); - -typedef enum UnescapeFlags { - UNESCAPE_RELAX = 1, -} UnescapeFlags; - -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); - -char *xescape(const char *s, const char *bad); - -char *ascii_strlower(char *path); - -bool dirent_is_file(const struct dirent *de) _pure_; -bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_; - -bool hidden_file(const char *filename) _pure_; - -bool chars_intersect(const char *a, const char *b) _pure_; - -/* 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]; \ - } - -ssize_t string_table_lookup(const char * const *table, size_t len, const char *key); - -#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(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#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) - -/* For string conversions where numbers are also acceptable */ -#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ - 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; \ - } \ - 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; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -int fd_nonblock(int fd, bool nonblock); -int fd_cloexec(int fd, bool cloexec); - -int close_all_fds(const int except[], unsigned n_except); - -bool fstype_is_network(const char *fstype); - -int flush_fd(int fd); - -int fopen_temporary(const char *path, FILE **_f, char **_temp_path); - -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); - -bool is_device_path(const char *path); - -int dir_is_empty(const char *path); -char* dirname_malloc(const char *path); - -char* lookup_uid(uid_t uid); -char* getlogname_malloc(void); -char* getusername_malloc(void); - -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); -int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); - -bool is_temporary_fs(const struct statfs *s) _pure_; -int fd_is_temporary_fs(int fd); - -int pipe_eof(int fd); - -#define xsprintf(buf, fmt, ...) \ - assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), \ - "xsprintf: " #buf "[] must be big enough") - -int files_same(const char *filea, const char *fileb); - -int running_in_chroot(void); - -char *ellipsize(const char *s, size_t length, unsigned percent); - /* bytes columns */ -char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent); - -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); - -noreturn void freeze(void); - -bool null_or_empty(struct stat *st) _pure_; -int null_or_empty_path(const char *fn); -int null_or_empty_fd(int fd); - -DIR *xopendirat(int dirfd, const char *name, int flags); - -char *fstab_node_to_udev_node(const char *p); - void execute_directories(const char* const* directories, usec_t timeout, char *argv[]); -bool nulstr_contains(const char*nulstr, const char *needle); - bool plymouth_running(void); -char* strshorten(char *s, size_t l); - -int symlink_idempotent(const char *from, const char *to); - -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 fchmod_umask(int fd, mode_t mode); - bool display_is_local(const char *display) _pure_; int socket_from_display(const char *display, char **path); -int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell); -int get_group_creds(const char **groupname, gid_t *gid); - -int in_gid(gid_t gid); -int in_group(const char *name); - -char* uid_to_name(uid_t uid); -char* gid_to_name(gid_t gid); - -int glob_exists(const char *path); -int glob_extend(char ***strv, const char *path); - -int dirent_ensure_type(DIR *d, struct dirent *de); - -int get_files_in_directory(const char *path, char ***list); - -char *strjoin(const char *x, ...) _sentinel_; - -bool is_main_thread(void); - -static inline bool _pure_ in_charset(const char *s, const char* charset) { - assert(s); - assert(charset); - return s[strspn(s, charset)] == '\0'; -} - int block_get_whole_disk(dev_t d, dev_t *ret); #define NULSTR_FOREACH(i, l) \ @@ -448,27 +74,6 @@ int block_get_whole_disk(dev_t d, dev_t *ret); #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)) -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 log_facility_unshifted_to_string_alloc(int i, char **s); -int log_facility_unshifted_from_string(const char *s); - -int log_level_to_string_alloc(int i, char **s); -int log_level_from_string(const char *s); - -int sched_policy_to_string_alloc(int i, char **s); -int sched_policy_from_string(const char *s); - -const char *rlimit_to_string(int i) _const_; -int rlimit_from_string(const char *s) _pure_; - -int ip_tos_to_string_alloc(int i, char **s); -int ip_tos_from_string(const char *s); - extern int saved_argc; extern char **saved_argv; @@ -476,182 +81,36 @@ bool kexec_loaded(void); int prot_from_flags(int flags) _const_; -char *format_bytes(char *buf, size_t l, uint64_t t); - -int fd_wait_for_event(int fd, int event, usec_t timeout); - -void* memdup(const void *p, size_t l) _alloc_(2); - -int fd_inc_sndbuf(int fd, size_t n); -int fd_inc_rcvbuf(int fd, size_t n); - int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...); -int setrlimit_closest(int resource, const struct rlimit *rlim); - -bool http_url_is_valid(const char *url) _pure_; -bool documentation_url_is_valid(const char *url) _pure_; - -bool http_etag_is_valid(const char *etag); - bool in_initrd(void); -int get_home_dir(char **ret); -int get_shell(char **_ret); - -static inline void freep(void *p) { - free(*(void**) p); -} - -static inline void closep(int *fd) { - safe_close(*fd); -} - -static inline void umaskp(mode_t *u) { - umask(*u); -} - -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_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent); - -#define _cleanup_free_ _cleanup_(freep) -#define _cleanup_close_ _cleanup_(closep) -#define _cleanup_umask_ _cleanup_(umaskp) -#define _cleanup_globfree_ _cleanup_(globfree) -#define _cleanup_fclose_ _cleanup_(fclosep) -#define _cleanup_pclose_ _cleanup_(pclosep) -#define _cleanup_closedir_ _cleanup_(closedirp) -#define _cleanup_endmntent_ _cleanup_(endmntentp) -#define _cleanup_close_pair_ _cleanup_(close_pairp) - -_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) { - if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) - return NULL; - - return malloc(a * b); -} - -_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) { - if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) - return NULL; - - return realloc(p, a * b); -} - -_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) { - if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) - return NULL; - - return memdup(p, a * b); -} - -bool filename_is_valid(const char *p) _pure_; -bool path_is_safe(const char *p) _pure_; -bool string_is_safe(const char *p) _pure_; -bool string_has_cc(const char *p, const char *ok) _pure_; - -/** - * Check if a string contains any glob patterns. - */ -_pure_ static inline bool string_is_glob(const char *p) { - return !!strpbrk(p, GLOB_CHARS); -} - void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, int (*compar) (const void *, const void *, void *), void *arg); -#define _(String) gettext (String) -#define N_(String) String -void init_gettext(void); -bool is_locale_utf8(void); - -typedef enum DrawSpecialChar { - DRAW_TREE_VERTICAL, - DRAW_TREE_BRANCH, - DRAW_TREE_RIGHT, - DRAW_TREE_SPACE, - DRAW_TRIANGULAR_BULLET, - DRAW_BLACK_CIRCLE, - DRAW_ARROW, - DRAW_DASH, - _DRAW_SPECIAL_CHAR_MAX -} DrawSpecialChar; - -const char *draw_special_char(DrawSpecialChar ch); - -char *strreplace(const char *text, const char *old_string, const char *new_string); +/** + * 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, comparison_fn_t compar) { + if (nmemb <= 1) + return; -char *strip_tab_ansi(char **p, size_t *l); + assert(base); + qsort(base, nmemb, size, compar); +} int on_ac_power(void); -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); - -#define FOREACH_LINE(line, f, on_error) \ - for (;;) \ - if (!fgets(line, sizeof(line), f)) { \ - if (ferror(f)) { \ - on_error; \ - } \ - break; \ - } else - -#define FOREACH_DIRENT(de, d, on_error) \ - for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \ - if (!de) { \ - if (errno > 0) { \ - on_error; \ - } \ - break; \ - } else if (hidden_file((de)->d_name)) \ - continue; \ - else - -#define FOREACH_DIRENT_ALL(de, d, on_error) \ - for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \ - if (!de) { \ - if (errno > 0) { \ - on_error; \ - } \ - break; \ - } else +#define memzero(x,l) (memset((x), 0, (l))) +#define zero(x) (memzero(&(x), sizeof(x))) static inline void *mempset(void *s, int c, size_t n) { memset(s, c, n); return (uint8_t*)s + n; } -char *hexmem(const void *p, size_t l); -int unhexmem(const char *p, size_t l, void **mem, size_t *len); - -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); - -char *base64mem(const void *p, size_t l); -int unbase64mem(const char *p, size_t l, void **mem, size_t *len); - -char *strextend(char **x, ...) _sentinel_; -char *strrep(const char *s, unsigned n); - -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])) - static inline void _reset_errno_(int *saved_errno) { errno = *saved_errno; } @@ -667,20 +126,6 @@ static inline int negative_errno(void) { return -errno; } -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) - static inline unsigned u64log2(uint64_t n) { #if __SIZEOF_LONG_LONG__ == 8 return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; @@ -718,228 +163,15 @@ static inline unsigned log2u_round_up(unsigned x) { return log2u(x - 1) + 1; } -static inline bool logind_running(void) { - return access("/run/systemd/seats/", F_OK) >= 0; -} - -#define DECIMAL_STR_WIDTH(x) \ - ({ \ - typeof(x) _x_ = (x); \ - unsigned ans = 1; \ - while (_x_ /= 10) \ - ans++; \ - ans; \ - }) - -int unlink_noerrno(const char *path); - -#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_); \ - }) - -#define strjoina(a, ...) \ - ({ \ - const char *_appendees_[] = { a, __VA_ARGS__ }; \ - char *_d_, *_p_; \ - int _len_ = 0; \ - unsigned _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_; \ - }) - bool id128_is_valid(const char *s) _pure_; -int split_pair(const char *s, const char *sep, char **l, char **r); - -int shall_restore_state(void); - -/** - * 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, comparison_fn_t compar) { - if (nmemb <= 1) - return; - - assert(base); - qsort(base, nmemb, size, compar); -} - -/* 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); -} - -int proc_cmdline(char **ret); -int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value)); -int get_proc_cmdline_key(const char *parameter, char **value); - 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); -int getpeercred(int fd, struct ucred *ucred); -int getpeersec(int fd, char **ret); - -int writev_safe(int fd, const struct iovec *w, int j); - -int mkostemp_safe(char *pattern, int flags); -int open_tmpfile(const char *path, int flags); - -int fd_warn_permissions(const char *path, int fd); - -#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); - uint64_t physical_memory(void); -void hexdump(FILE *f, const void *p, size_t s); - -union file_handle_union { - struct file_handle handle; - char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ]; -}; -#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ } - int update_reboot_param_file(const char *param); -int umount_recursive(const char *target, int flags); - -int bind_remount_recursive(const char *prefix, bool ro); - -int fflush_and_check(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 take_password_lock(const char *root); - -int is_symlink(const char *path); -int is_dir(const char *path, bool follow); -int is_device_node(const char *path); - -typedef enum ExtractFlags { - EXTRACT_RELAX = 1, - EXTRACT_CUNESCAPE = 2, - EXTRACT_CUNESCAPE_RELAX = 4, - EXTRACT_QUOTES = 8, - EXTRACT_DONT_COALESCE_SEPARATORS = 16, -} 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, ExtractFlags flags, ...) _sentinel_; - -int free_and_strdup(char **p, const char *s); - -#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]; -}; - -#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) - -int ptsname_malloc(int fd, char **ret); - -int openpt_in_namespace(pid_t pid, int flags); - -ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags); - -int fd_setcrtime(int fd, usec_t usec); -int fd_getcrtime(int fd, usec_t *usec); -int path_getcrtime(const char *p, usec_t *usec); -int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags); - -int same_fd(int a, int b); - -int chattr_fd(int fd, unsigned value, unsigned mask); -int chattr_path(const char *p, unsigned value, unsigned mask); - -int read_attr_fd(int fd, unsigned *ret); -int read_attr_path(const char *p, unsigned *ret); - -#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim }) - -ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); - -void sigkill_wait(pid_t *pid); -#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait) - -int syslog_parse_priority(const char **p, int *priority, bool with_facility); - -void cmsg_close_all(struct msghdr *mh); - -int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); - -char *shell_escape(const char *s, const char *bad); -char *shell_maybe_quote(const char *s); - -int parse_mode(const char *s, mode_t *ret); - -int mount_move_root(const char *path); - -int reset_uid_gid(void); - -int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink); -int fgetxattr_malloc(int fd, const char *name, char **value); - -int send_one_fd(int transport_fd, int fd, int flags); -int receive_one_fd(int transport_fd, int flags); - -void nop_signal_handler(int sig); - int version(void); - -bool fdname_is_valid(const char *s); diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c index 2f5b9b3731..4660c7ea09 100644 --- a/src/systemd/src/libsystemd-network/arp-util.c +++ b/src/systemd/src/libsystemd-network/arp-util.c @@ -21,8 +21,9 @@ #include <linux/filter.h> #include <arpa/inet.h> -#include "util.h" #include "arp-util.h" +#include "fd-util.h" +#include "util.h" int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { struct sock_filter filter[] = { diff --git a/src/systemd/src/libsystemd-network/arp-util.h b/src/systemd/src/libsystemd-network/arp-util.h index 44e5c893a7..63c559f8dd 100644 --- a/src/systemd/src/libsystemd-network/arp-util.h +++ b/src/systemd/src/libsystemd-network/arp-util.h @@ -23,8 +23,8 @@ #include <netinet/if_ether.h> -#include "sparse-endian.h" #include "socket-util.h" +#include "sparse-endian.h" int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac); diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index 7d9cad2a70..d7ae865557 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -19,24 +19,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ - -#include "sd-id128.h" #include "libudev.h" -#include "udev-util.h" - -#include "virt.h" -#include "sparse-endian.h" -#include "siphash24.h" +#include "sd-id128.h" -#include "dhcp6-protocol.h" #include "dhcp-identifier.h" +#include "dhcp6-protocol.h" #include "network-internal.h" +#include "siphash24.h" +#include "sparse-endian.h" +#include "udev-util.h" +#include "virt.h" #define SYSTEMD_PEN 43793 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { sd_id128_t machine_id; + uint64_t hash; int r; assert(duid); @@ -52,13 +51,13 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { *len = sizeof(duid->type) + sizeof(duid->en); /* a bit of snake-oil perhaps, but no need to expose the machine-id - directly */ - siphash24(duid->en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes); + directly; duid->en.id might not be aligned, so we need to copy */ + hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); + memcpy(duid->en.id, &hash, sizeof(duid->en.id)); return 0; } - int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) { /* name is a pointer to memory in the udev_device struct, so must have the same scope */ @@ -87,10 +86,12 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i } if (name) - siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes); + id = siphash24(name, strlen(name), HASH_KEY.bytes); else /* fall back to MAC address if no predictable name available */ - siphash24((uint8_t*)&id, mac, mac_len, HASH_KEY.bytes); + id = siphash24(mac, mac_len, HASH_KEY.bytes); + + id = htole64(id); /* fold into 32 bits */ unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32)); diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h index 95117915f4..2291736f8b 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.h +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h @@ -21,11 +21,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "sd-id128.h" #include "macro.h" #include "sparse-endian.h" #include "unaligned.h" -#include "sd-id128.h" /* RFC 3315 section 9.1: * A DUID can be no more than 128 octets long (not including the type code). diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h index df6f882af5..a5daaa543a 100644 --- a/src/systemd/src/libsystemd-network/dhcp-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-internal.h @@ -22,15 +22,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdint.h> #include <linux/if_packet.h> -#include <net/if_arp.h> #include <net/ethernet.h> - -#include "socket-util.h" +#include <net/if_arp.h> +#include <stdint.h> #include "sd-dhcp-client.h" + #include "dhcp-protocol.h" +#include "socket-util.h" int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link, uint32_t xid, const uint8_t *mac_addr, diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h index c6b97ca8f7..138bdd9691 100644 --- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h @@ -25,12 +25,11 @@ #include <stdint.h> #include <linux/if_packet.h> -#include "util.h" -#include "list.h" +#include "sd-dhcp-client.h" #include "dhcp-protocol.h" - -#include "sd-dhcp-client.h" +#include "list.h" +#include "util.h" struct sd_dhcp_route { struct in_addr dst_addr; diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index 7f10838de1..fac25e0fa2 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -18,18 +18,18 @@ ***/ #include <errno.h> -#include <sys/socket.h> -#include <string.h> -#include <linux/if_packet.h> -#include <linux/if_infiniband.h> #include <net/ethernet.h> #include <net/if_arp.h> #include <stdio.h> +#include <string.h> +#include <sys/socket.h> #include <linux/filter.h> - -#include "socket-util.h" +#include <linux/if_infiniband.h> +#include <linux/if_packet.h> #include "dhcp-internal.h" +#include "fd-util.h" +#include "socket-util.h" static int _bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid, const uint8_t *mac_addr, diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c index 36be7d54ed..a6c410ba91 100644 --- a/src/systemd/src/libsystemd-network/dhcp-option.c +++ b/src/systemd/src/libsystemd-network/dhcp-option.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdint.h> -#include <string.h> #include <errno.h> +#include <stdint.h> #include <stdio.h> +#include <string.h> #include "dhcp-internal.h" diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c index cd7f5095ca..9ff42b155e 100644 --- a/src/systemd/src/libsystemd-network/dhcp-packet.c +++ b/src/systemd/src/libsystemd-network/dhcp-packet.c @@ -19,13 +19,12 @@ ***/ #include <errno.h> -#include <string.h> #include <net/ethernet.h> #include <net/if_arp.h> +#include <string.h> - -#include "dhcp-protocol.h" #include "dhcp-internal.h" +#include "dhcp-protocol.h" #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 diff --git a/src/systemd/src/libsystemd-network/dhcp-protocol.h b/src/systemd/src/libsystemd-network/dhcp-protocol.h index 88a81d2866..05bb5ae493 100644 --- a/src/systemd/src/libsystemd-network/dhcp-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp-protocol.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/udp.h> #include <netinet/ip.h> +#include <netinet/udp.h> #include <stdint.h> #include "macro.h" @@ -137,6 +137,7 @@ enum { DHCP_OPTION_REBINDING_T2_TIME = 59, DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, DHCP_OPTION_CLIENT_IDENTIFIER = 61, + DHCP_OPTION_FQDN = 81, DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, @@ -144,3 +145,12 @@ enum { DHCP_OPTION_PRIVATE_LAST = 254, DHCP_OPTION_END = 255, }; + +#define DHCP_MAX_FQDN_LENGTH 255 + +enum { + DHCP_FQDN_FLAG_S = (1 << 0), + DHCP_FQDN_FLAG_O = (1 << 1), + DHCP_FQDN_FLAG_E = (1 << 2), + DHCP_FQDN_FLAG_N = (1 << 3), +}; diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index 83e8192f58..ecc220f2f6 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -24,10 +24,11 @@ #include <net/ethernet.h> #include <netinet/in.h> -#include "sparse-endian.h" #include "sd-event.h" + #include "list.h" #include "macro.h" +#include "sparse-endian.h" typedef struct DHCP6Address DHCP6Address; @@ -58,9 +59,6 @@ typedef struct DHCP6IA DHCP6IA; #define log_dhcp6_client(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) -int dhcp_network_icmp6_bind_router_solicitation(int index); -int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr); - int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, size_t optlen, const void *optval); int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia); diff --git a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h index 4edecf7711..f6cf0b30d3 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h @@ -25,6 +25,7 @@ #include <stdint.h> #include "sd-dhcp6-lease.h" + #include "dhcp6-internal.h" struct sd_dhcp6_lease { diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c index 187975364b..fd2d60c9d5 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-network.c +++ b/src/systemd/src/libsystemd-network/dhcp6-network.c @@ -18,148 +18,47 @@ ***/ #include <errno.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <string.h> -#include <linux/if_packet.h> +#include <netinet/in.h> +#include <netinet/ip6.h> #include <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> #include <unistd.h> -#include <netinet/ip6.h> -#include <netinet/icmp6.h> -#include <netinet/in.h> - -#include "socket-util.h" +#include <linux/if_packet.h> #include "dhcp6-internal.h" #include "dhcp6-protocol.h" - -#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \ - { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } } - -#define IN6ADDR_ALL_NODES_MULTICAST_INIT \ - { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } } - -int dhcp_network_icmp6_bind_router_solicitation(int index) { - struct icmp6_filter filter = { }; - struct ipv6_mreq mreq = { - .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT, - .ipv6mr_interface = index, - }; - _cleanup_close_ int s = -1; - int r, zero = 0, hops = 255; - - s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, - IPPROTO_ICMPV6); - if (s < 0) - return -errno; - - ICMP6_FILTER_SETBLOCKALL(&filter); - ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); - r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, - sizeof(filter)); - if (r < 0) - return -errno; - - /* RFC 3315, section 6.7, bullet point 2 may indicate that an - IPV6_PKTINFO socket option also applies for ICMPv6 multicast. - Empirical experiments indicates otherwise and therefore an - IPV6_MULTICAST_IF socket option is used here instead */ - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, - sizeof(index)); - if (r < 0) - return -errno; - - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, - sizeof(zero)); - if (r < 0) - return -errno; - - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, - sizeof(hops)); - if (r < 0) - return -errno; - - r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, - sizeof(mreq)); - if (r < 0) - return -errno; - - r = s; - s = -1; - return r; -} - -int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { - struct sockaddr_in6 dst = { - .sin6_family = AF_INET6, - .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT, - }; - struct { - struct nd_router_solicit rs; - struct nd_opt_hdr rs_opt; - struct ether_addr rs_opt_mac; - } _packed_ rs = { - .rs.nd_rs_type = ND_ROUTER_SOLICIT, - }; - struct iovec iov[1] = { - { &rs, }, - }; - struct msghdr msg = { - .msg_name = &dst, - .msg_namelen = sizeof(dst), - .msg_iov = iov, - .msg_iovlen = 1, - }; - int r; - - if (ether_addr) { - memcpy(&rs.rs_opt_mac, ether_addr, ETH_ALEN); - rs.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR; - rs.rs_opt.nd_opt_len = 1; - iov[0].iov_len = sizeof(rs); - } else - iov[0].iov_len = sizeof(rs.rs); - - r = sendmsg(s, &msg, 0); - if (r < 0) - return -errno; - - return 0; -} +#include "fd-util.h" +#include "socket-util.h" int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { - struct in6_pktinfo pktinfo = { - .ipi6_ifindex = index, - }; union sockaddr_union src = { .in6.sin6_family = AF_INET6, .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), - .in6.sin6_addr = IN6ADDR_ANY_INIT, + .in6.sin6_scope_id = index, }; _cleanup_close_ int s = -1; int r, off = 0, on = 1; - if (local_address) - memcpy(&src.in6.sin6_addr, local_address, - sizeof(src.in6.sin6_addr)); + assert(index > 0); + assert(local_address); - s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, - IPPROTO_UDP); + src.in6.sin6_addr = *local_address; + + s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP); if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo, - sizeof(pktinfo)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); + r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (r < 0) return -errno; diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index f41bebced0..62023a9e49 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -19,18 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/in.h> #include <errno.h> +#include <netinet/in.h> #include <string.h> -#include "sparse-endian.h" -#include "unaligned.h" -#include "util.h" -#include "strv.h" - +#include "alloc-util.h" #include "dhcp6-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" +#include "sparse-endian.h" +#include "strv.h" +#include "unaligned.h" +#include "util.h" #define DHCP6_OPTION_IA_NA_LEN 12 #define DHCP6_OPTION_IA_TA_LEN 4 @@ -344,7 +344,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * int r; assert_return(optlen > 1, -ENODATA); - assert_return(optval[optlen] == '\0', -EINVAL); + assert_return(optval[optlen - 1] == '\0', -EINVAL); while (pos < optlen) { _cleanup_free_ char *ret = NULL; diff --git a/src/systemd/src/libsystemd-network/lldp-internal.c b/src/systemd/src/libsystemd-network/lldp-internal.c index 4012cd483b..583be2f55d 100644 --- a/src/systemd/src/libsystemd-network/lldp-internal.c +++ b/src/systemd/src/libsystemd-network/lldp-internal.c @@ -20,9 +20,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "lldp-internal.h" #include "sd-lldp.h" +#include "alloc-util.h" +#include "lldp-internal.h" + /* We store maximum 1K chassis entries */ #define LLDP_MIB_MAX_CHASSIS 1024 diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h index 284cc6720e..5d19fa0fea 100644 --- a/src/systemd/src/libsystemd-network/lldp-internal.h +++ b/src/systemd/src/libsystemd-network/lldp-internal.h @@ -22,11 +22,12 @@ #pragma once -#include "log.h" +#include "sd-event.h" + #include "list.h" #include "lldp-tlv.h" +#include "log.h" #include "prioq.h" -#include "sd-event.h" typedef struct lldp_neighbour_port lldp_neighbour_port; typedef struct lldp_chassis lldp_chassis; diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c index 12a6599ff1..f483cd9c8e 100644 --- a/src/systemd/src/libsystemd-network/lldp-network.c +++ b/src/systemd/src/libsystemd-network/lldp-network.c @@ -23,10 +23,11 @@ #include <linux/filter.h> #include <linux/if_ether.h> -#include "socket-util.h" -#include "lldp-tlv.h" -#include "lldp-network.h" +#include "fd-util.h" #include "lldp-internal.h" +#include "lldp-network.h" +#include "lldp-tlv.h" +#include "socket-util.h" int lldp_network_bind_raw_socket(int ifindex) { typedef struct LLDPFrame { diff --git a/src/systemd/src/libsystemd-network/lldp-port.c b/src/systemd/src/libsystemd-network/lldp-port.c index 7486b4c38f..1f1a49adbf 100644 --- a/src/systemd/src/libsystemd-network/lldp-port.c +++ b/src/systemd/src/libsystemd-network/lldp-port.c @@ -20,10 +20,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "alloc-util.h" #include "async.h" -#include "lldp-port.h" -#include "lldp-network.h" #include "lldp-internal.h" +#include "lldp-network.h" +#include "lldp-port.h" int lldp_port_start(lldp_port *p) { int r; diff --git a/src/systemd/src/libsystemd-network/lldp-tlv.c b/src/systemd/src/libsystemd-network/lldp-tlv.c index 66af22e37d..66343147a1 100644 --- a/src/systemd/src/libsystemd-network/lldp-tlv.c +++ b/src/systemd/src/libsystemd-network/lldp-tlv.c @@ -20,11 +20,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <net/ethernet.h> #include <arpa/inet.h> +#include <net/ethernet.h> -#include "macro.h" +#include "alloc-util.h" #include "lldp-tlv.h" +#include "macro.h" int tlv_section_new(tlv_section **ret) { tlv_section *s; @@ -277,7 +278,7 @@ int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) { p = m->pdu; - /* extract ethernet header */ + /* extract Ethernet header */ memcpy(&m->mac, p, ETH_ALEN); p += sizeof(struct ether_header); @@ -386,12 +387,11 @@ static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t r = lldp_tlv_packet_enter_container(tlv, type); if (r < 0) - goto out; + return r; r = tlv_packet_read_u16(tlv, value); r2 = lldp_tlv_packet_exit_container(tlv); - out: return r < 0 ? r : r2; } @@ -428,18 +428,18 @@ int sd_lldp_packet_read_chassis_id(tlv_packet *tlv, r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID); if (r < 0) - goto out2; + return r; r = tlv_packet_read_u8(tlv, &subtype); if (r < 0) - goto out1; + goto out; switch (subtype) { case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: r = tlv_packet_read_bytes(tlv, data, length); if (r < 0) - goto out1; + goto out; break; default: @@ -449,10 +449,9 @@ int sd_lldp_packet_read_chassis_id(tlv_packet *tlv, *type = subtype; - out1: + out: r2 = lldp_tlv_packet_exit_container(tlv); - out2: return r < 0 ? r : r2; } @@ -468,11 +467,11 @@ int sd_lldp_packet_read_port_id(tlv_packet *tlv, r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID); if (r < 0) - goto out2; + return r; r = tlv_packet_read_u8(tlv, &subtype); if (r < 0) - goto out1; + goto out; switch (subtype) { case LLDP_PORT_SUBTYPE_PORT_COMPONENT: @@ -482,7 +481,7 @@ int sd_lldp_packet_read_port_id(tlv_packet *tlv, r = tlv_packet_read_string(tlv, &s, length); if (r < 0) - goto out1; + goto out; *data = (uint8_t *) s; @@ -491,7 +490,7 @@ int sd_lldp_packet_read_port_id(tlv_packet *tlv, r = tlv_packet_read_bytes(tlv, data, length); if (r < 0) - goto out1; + goto out; break; default: @@ -501,10 +500,9 @@ int sd_lldp_packet_read_port_id(tlv_packet *tlv, *type = subtype; - out1: + out: r2 = lldp_tlv_packet_exit_container(tlv); - out2: return r < 0 ? r : r2; } @@ -541,12 +539,11 @@ int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) { r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID); if (r < 0) - goto out; + return r; r = tlv_packet_read_u16(tlv, id); r2 = lldp_tlv_packet_exit_container(tlv); - out: return r < 0 ? r : r2; } @@ -557,7 +554,7 @@ int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flag r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID); if (r < 0) - goto out; + return r; r = tlv_packet_read_u8(tlv, flags); if (r >= 0) @@ -565,7 +562,6 @@ int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flag r2 = lldp_tlv_packet_exit_container(tlv); - out: return r < 0 ? r : r2; } @@ -577,7 +573,7 @@ int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **nam r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME); if (r < 0) - goto out; + return r; r = tlv_packet_read_u16(tlv, vlan_id); if (r >= 0) @@ -590,7 +586,6 @@ int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **nam r2 = lldp_tlv_packet_exit_container(tlv); - out: return r < 0 ? r : r2; } @@ -601,12 +596,11 @@ int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) { r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID); if (r < 0) - goto out; + return r; r = tlv_packet_read_u16(tlv, id); r2 = lldp_tlv_packet_exit_container(tlv); - out: return r < 0 ? r : r2; } @@ -617,7 +611,7 @@ int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, u r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION); if (r < 0) - goto out; + return r; r = tlv_packet_read_u8(tlv, status); if (r >= 0) @@ -625,7 +619,6 @@ int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, u r2 = lldp_tlv_packet_exit_container(tlv); - out: return r < 0 ? r : r2; } diff --git a/src/systemd/src/libsystemd-network/lldp-tlv.h b/src/systemd/src/libsystemd-network/lldp-tlv.h index 2d2c776be6..f5cd77477f 100644 --- a/src/systemd/src/libsystemd-network/lldp-tlv.h +++ b/src/systemd/src/libsystemd-network/lldp-tlv.h @@ -24,18 +24,18 @@ #include <net/ethernet.h> -#include "util.h" -#include "lldp.h" -#include "list.h" - #include "sd-lldp.h" -typedef struct tlv_packet tlv_packet; -typedef struct tlv_section tlv_section; +#include "list.h" +#include "lldp.h" +#include "util.h" + +typedef struct sd_lldp_packet tlv_packet; +typedef struct sd_lldp_section tlv_section; #define LLDP_OUI_LEN 3 -struct tlv_section { +struct sd_lldp_section { uint16_t type; uint16_t length; uint8_t *oui; @@ -54,7 +54,7 @@ struct tlv_section { int tlv_section_new(tlv_section **ret); void tlv_section_free(tlv_section *ret); -struct tlv_packet { +struct sd_lldp_packet { unsigned n_ref; uint16_t type; diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index 2a62af2fd4..a4d4f1ae2f 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -19,20 +19,25 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/ether.h> -#include <linux/if.h> #include <arpa/inet.h> +#include <linux/if.h> +#include <netinet/ether.h> -#include "strv.h" -#include "siphash24.h" +#include "sd-ndisc.h" + +#include "alloc-util.h" +#include "condition.h" +#include "conf-parser.h" #include "dhcp-lease-internal.h" +#include "hexdecoct.h" #include "log.h" +#include "network-internal.h" +#include "parse-util.h" +#include "siphash24.h" +#include "string-util.h" +#include "strv.h" #include "utf8.h" #include "util.h" -#include "conf-parser.h" -#include "condition.h" -#include "network-internal.h" -#include "sd-icmp6-nd.h" const char *net_get_name(struct udev_device *device) { const char *name, *field; @@ -51,7 +56,7 @@ const char *net_get_name(struct udev_device *device) { #define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a) -int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]) { +int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result) { size_t l, sz = 0; const char *name = NULL; int r; @@ -76,7 +81,7 @@ int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8 /* Let's hash the machine ID plus the device name. We * use a fixed, but originally randomly created hash * key here. */ - siphash24(result, v, sz, HASH_KEY.bytes); + *result = htole64(siphash24(v, sz, HASH_KEY.bytes)); return 0; } @@ -390,8 +395,8 @@ void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, assert(size); for (i = 0; i < size; i++) - fprintf(f, SD_ICMP6_ND_ADDRESS_FORMAT_STR"%s", - SD_ICMP6_ND_ADDRESS_FORMAT_VAL(addresses[i]), + fprintf(f, SD_NDISC_ADDRESS_FORMAT_STR"%s", + SD_NDISC_ADDRESS_FORMAT_VAL(addresses[i]), (i < (size - 1)) ? " ": ""); } diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index d5d4ef42f2..8a30921966 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -23,8 +23,8 @@ #include <stdbool.h> -#include "udev.h" #include "condition.h" +#include "udev.h" bool net_match_config(const struct ether_addr *match_mac, char * const *match_path, @@ -62,7 +62,7 @@ int config_parse_ifalias(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]); +int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result); const char *net_get_name(struct udev_device *device); void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 141b836a0d..5ec0e661f7 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -17,24 +17,28 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> #include <errno.h> -#include <string.h> -#include <stdio.h> #include <net/ethernet.h> #include <net/if_arp.h> -#include <linux/if_infiniband.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/ioctl.h> +#include <linux/if_infiniband.h> -#include "util.h" -#include "random-util.h" -#include "async.h" +#include "sd-dhcp-client.h" -#include "dhcp-protocol.h" +#include "alloc-util.h" +#include "async.h" +#include "dhcp-identifier.h" #include "dhcp-internal.h" #include "dhcp-lease-internal.h" -#include "dhcp-identifier.h" -#include "sd-dhcp-client.h" +#include "dhcp-protocol.h" +#include "dns-domain.h" +#include "hostname-util.h" +#include "random-util.h" +#include "string-util.h" +#include "util.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) @@ -296,6 +300,9 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client, assert_return(client, -EINVAL); + if (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname)) + return -EINVAL; + if (streq_ptr(client->hostname, hostname)) return 0; @@ -537,6 +544,24 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, return 0; } +static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset, + const char *fqdn) { + uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH]; + int r; + + buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */ + DHCP_FQDN_FLAG_E; /* Canonical wire format */ + buffer[1] = 0; /* RCODE1 (deprecated) */ + buffer[2] = 0; /* RCODE2 (deprecated) */ + + r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3); + if (r > 0) + r = dhcp_option_append(message, optlen, optoffset, 0, + DHCP_OPTION_FQDN, 3 + r, buffer); + + return r; +} + static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet, size_t len) { dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT, @@ -574,13 +599,21 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } - /* it is unclear from RFC 2131 if client should send hostname in - DHCPDISCOVER but dhclient does and so we do as well - */ if (client->hostname) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_HOST_NAME, - strlen(client->hostname), client->hostname); + /* According to RFC 4702 "clients that send the Client FQDN option in + their messages MUST NOT also send the Host Name option". Just send + one of the two depending on the hostname type. + */ + if (dns_name_single_label(client->hostname)) { + /* it is unclear from RFC 2131 if client should send hostname in + DHCPDISCOVER but dhclient does and so we do as well + */ + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_HOST_NAME, + strlen(client->hostname), client->hostname); + } else + r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset, + client->hostname); if (r < 0) return r; } @@ -686,9 +719,13 @@ static int client_send_request(sd_dhcp_client *client) { } if (client->hostname) { - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_HOST_NAME, - strlen(client->hostname), client->hostname); + if (dns_name_single_label(client->hostname)) + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_HOST_NAME, + strlen(client->hostname), client->hostname); + else + r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset, + client->hostname); if (r < 0) return r; } @@ -1265,8 +1302,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return r; log_dhcp_client(client, "lease expires in %s", - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - lifetime_timeout - time_now, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_timeout - time_now, USEC_PER_SEC)); /* don't arm earlier timeouts if this has already expired */ if (lifetime_timeout <= time_now) @@ -1292,8 +1328,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return r; log_dhcp_client(client, "T2 expires in %s", - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - t2_timeout - time_now, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, t2_timeout - time_now, USEC_PER_SEC)); /* don't arm earlier timeout if this has already expired */ if (t2_timeout <= time_now) @@ -1318,8 +1353,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return r; log_dhcp_client(client, "T1 expires in %s", - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - t1_timeout - time_now, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, USEC_PER_SEC)); return 0; } @@ -1518,7 +1552,7 @@ static int client_receive_message_udp(sd_event_source *s, int fd, expected_hlen = ETH_ALEN; expected_chaddr = (const struct ether_addr *) &client->mac_addr; } else { - /* Non-ethernet links expect zero chaddr */ + /* Non-Ethernet links expect zero chaddr */ expected_hlen = 0; expected_chaddr = &zero_mac; } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index df3d8e6e3c..8befedc500 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -18,21 +18,27 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> +#include <arpa/inet.h> #include <errno.h> -#include <string.h> #include <stdio.h> -#include <arpa/inet.h> +#include <stdlib.h> +#include <string.h> + +#include "sd-dhcp-lease.h" +#include "alloc-util.h" +#include "dhcp-lease-internal.h" +#include "dhcp-protocol.h" +#include "dns-domain.h" +#include "fd-util.h" #include "fileio.h" -#include "unaligned.h" -#include "in-addr-util.h" +#include "hexdecoct.h" #include "hostname-util.h" -#include "dns-domain.h" +#include "in-addr-util.h" #include "network-internal.h" -#include "dhcp-protocol.h" -#include "dhcp-lease-internal.h" -#include "sd-dhcp-lease.h" +#include "parse-util.h" +#include "string-util.h" +#include "unaligned.h" int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { assert_return(lease, -EINVAL); @@ -945,19 +951,19 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (address) { r = inet_pton(AF_INET, address, &lease->address); if (r <= 0) - log_debug_errno(errno, "Failed to parse address %s, ignoring: %m", address); + log_debug("Failed to parse address %s, ignoring.", address); } if (router) { r = inet_pton(AF_INET, router, &lease->router); if (r <= 0) - log_debug_errno(errno, "Failed to parse router %s, ignoring: %m", router); + log_debug("Failed to parse router %s, ignoring.", router); } if (netmask) { r = inet_pton(AF_INET, netmask, &lease->subnet_mask); if (r <= 0) - log_debug_errno(errno, "Failed to parse netmask %s, ignoring: %m", netmask); + log_debug("Failed to parse netmask %s, ignoring.", netmask); else lease->have_subnet_mask = true; } @@ -965,19 +971,19 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (server_address) { r = inet_pton(AF_INET, server_address, &lease->server_address); if (r <= 0) - log_debug_errno(errno, "Failed to parse netmask %s, ignoring: %m", server_address); + log_debug("Failed to parse server address %s, ignoring.", server_address); } if (next_server) { r = inet_pton(AF_INET, next_server, &lease->next_server); if (r <= 0) - log_debug_errno(errno, "Failed to parse next server %s, ignoring: %m", next_server); + log_debug("Failed to parse next server %s, ignoring.", next_server); } if (broadcast) { r = inet_pton(AF_INET, broadcast, &lease->broadcast); if (r <= 0) - log_debug_errno(errno, "Failed to parse broadcast address %s, ignoring: %m", broadcast); + log_debug("Failed to parse broadcast address %s, ignoring.", broadcast); else lease->have_broadcast = true; } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index acb31a16c2..801331d270 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -24,17 +24,19 @@ #include <sys/ioctl.h> #include <linux/if_infiniband.h> -#include "udev.h" -#include "udev-util.h" -#include "util.h" -#include "random-util.h" - -#include "network-internal.h" #include "sd-dhcp6-client.h" -#include "dhcp6-protocol.h" + +#include "alloc-util.h" +#include "dhcp-identifier.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" -#include "dhcp-identifier.h" +#include "dhcp6-protocol.h" +#include "fd-util.h" +#include "in-addr-util.h" +#include "network-internal.h" +#include "random-util.h" +#include "string-table.h" +#include "util.h" #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN @@ -45,6 +47,7 @@ struct sd_dhcp6_client { sd_event *event; int event_priority; int index; + struct in6_addr local_address; uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; uint16_t arp_type; @@ -132,6 +135,18 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) { return 0; } +int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address) { + assert_return(client, -EINVAL); + assert_return(local_address, -EINVAL); + assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL); + + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + + client->local_address = *local_address; + + return 0; +} + int sd_dhcp6_client_set_mac( sd_dhcp6_client *client, const uint8_t *addr, size_t addr_len, @@ -208,9 +223,8 @@ int sd_dhcp6_client_set_duid( return 0; } -int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enabled) { +int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) { assert_return(client, -EINVAL); - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); client->information_request = enabled; @@ -218,7 +232,7 @@ int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enable return 0; } -int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, bool *enabled) { +int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) { assert_return(client, -EINVAL); assert_return(enabled, -EINVAL); @@ -259,12 +273,12 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); - assert_return(ret, -EINVAL); if (!client->lease) return -ENOMSG; - *ret = client->lease; + if (ret) + *ret = client->lease; return 0; } @@ -595,8 +609,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, } log_dhcp6_client(client, "Next retransmission in %s", - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - client->retransmit_time, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC)); r = sd_event_add_time(client->event, &client->timeout_resend, clock_boottime_or_monotonic(), @@ -1048,9 +1061,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC); log_dhcp6_client(client, "T1 expires in %s", - format_timespan(time_string, - FORMAT_TIMESPAN_MAX, - timeout, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); r = sd_event_add_time(client->event, &client->lease->ia.timeout_t1, @@ -1072,9 +1083,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC); log_dhcp6_client(client, "T2 expires in %s", - format_timespan(time_string, - FORMAT_TIMESPAN_MAX, - timeout, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); r = sd_event_add_time(client->event, &client->lease->ia.timeout_t2, @@ -1120,11 +1129,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { } int sd_dhcp6_client_stop(sd_dhcp6_client *client) { + assert_return(client, -EINVAL); + client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); return 0; } +int sd_dhcp6_client_is_running(sd_dhcp6_client *client) { + assert_return(client, -EINVAL); + + return client->state != DHCP6_STATE_STOPPED; +} + int sd_dhcp6_client_start(sd_dhcp6_client *client) { int r = 0; enum DHCP6State state = DHCP6_STATE_SOLICITATION; @@ -1132,9 +1149,10 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); assert_return(client->index > 0, -EINVAL); + assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL); if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) - return -EALREADY; + return -EBUSY; r = client_reset(client); if (r < 0) @@ -1148,7 +1166,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (r < 0) return r; - r = dhcp6_network_bind_udp_socket(client->index, NULL); + r = dhcp6_network_bind_udp_socket(client->index, &client->local_address); if (r < 0) return r; diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index f34af6eaba..3f32ba35e7 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -22,11 +22,11 @@ #include <errno.h> -#include "strv.h" -#include "util.h" - +#include "alloc-util.h" #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" +#include "strv.h" +#include "util.h" int dhcp6_lease_clear_timers(DHCP6IA *ia) { assert_return(ia, -EINVAL); diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c index 95b96bfd52..5340fdc0c1 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -24,17 +24,19 @@ #include <stdlib.h> #include <string.h> +#include "sd-ipv4acd.h" + +#include "alloc-util.h" +#include "arp-util.h" #include "event-util.h" +#include "fd-util.h" #include "in-addr-util.h" #include "list.h" -#include "refcnt.h" #include "random-util.h" +#include "refcnt.h" #include "siphash24.h" #include "util.h" -#include "arp-util.h" -#include "sd-ipv4acd.h" - /* Constants from the RFC */ #define PROBE_WAIT 1 #define PROBE_NUM 3 @@ -468,7 +470,7 @@ int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){ return 0; } -bool sd_ipv4acd_is_running(sd_ipv4acd *ll) { +int sd_ipv4acd_is_running(sd_ipv4acd *ll) { assert_return(ll, false); return ll->state != IPV4ACD_STATE_INIT; diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c index dd427ddd78..30a7ef5785 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -18,13 +18,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> +#include <arpa/inet.h> #include <errno.h> -#include <string.h> #include <stdio.h> -#include <arpa/inet.h> +#include <stdlib.h> +#include <string.h> + +#include "sd-ipv4acd.h" +#include "sd-ipv4ll.h" +#include "alloc-util.h" #include "event-util.h" +#include "in-addr-util.h" #include "list.h" #include "random-util.h" #include "refcnt.h" @@ -32,9 +37,6 @@ #include "sparse-endian.h" #include "util.h" -#include "sd-ipv4acd.h" -#include "sd-ipv4ll.h" - #define IPV4LL_NETWORK 0xA9FE0000L #define IPV4LL_NETMASK 0xFFFF0000L @@ -99,6 +101,8 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) { if (!ll) return -ENOMEM; + ll->n_ref = 1; + r = sd_ipv4acd_new(&ll->acd); if (r < 0) return r; @@ -107,8 +111,6 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) { if (r < 0) return r; - ll->n_ref = 1; - *ret = ll; ll = NULL; @@ -141,15 +143,14 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { assert_return(ll, -EINVAL); if (!ll->random_data) { - uint8_t seed[8]; + uint64_t seed; /* If no random data is set, generate some from the MAC */ - siphash24(seed, &addr->ether_addr_octet, - ETH_ALEN, HASH_KEY.bytes); + seed = siphash24(&addr->ether_addr_octet, ETH_ALEN, HASH_KEY.bytes); assert_cc(sizeof(unsigned) <= 8); - r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed); + r = sd_ipv4ll_set_address_seed(ll, (unsigned) htole64(seed)); if (r < 0) return r; } @@ -226,12 +227,45 @@ int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) { return 0; } -bool sd_ipv4ll_is_running(sd_ipv4ll *ll) { +int sd_ipv4ll_is_running(sd_ipv4ll *ll) { assert_return(ll, false); return sd_ipv4acd_is_running(ll->acd); } +static bool ipv4ll_address_is_valid(const struct in_addr *address) { + uint32_t addr; + + assert(address); + + if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address)) + return false; + + addr = be32toh(address->s_addr); + + if ((addr & 0x0000FF00) == 0x0000 || + (addr & 0x0000FF00) == 0xFF00) + return false; + + return true; +} + +int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) { + int r; + + assert_return(ll, -EINVAL); + assert_return(address, -EINVAL); + assert_return(ipv4ll_address_is_valid(address), -EINVAL); + + r = sd_ipv4acd_set_address(ll->acd, address); + if (r < 0) + return r; + + ll->address = address->s_addr; + + return 0; +} + static int ipv4ll_pick_address(sd_ipv4ll *ll) { struct in_addr in_addr; be32_t addr; @@ -247,18 +281,15 @@ static int ipv4ll_pick_address(sd_ipv4ll *ll) { return r; addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK); } while (addr == ll->address || - (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK || (ntohl(addr) & 0x0000FF00) == 0x0000 || (ntohl(addr) & 0x0000FF00) == 0xFF00); in_addr.s_addr = addr; - r = sd_ipv4acd_set_address(ll->acd, &in_addr); + r = sd_ipv4ll_set_address(ll, &in_addr); if (r < 0) return r; - ll->address = addr; - return 0; } diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c index 06949a1e83..4ebe8053fa 100644 --- a/src/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/systemd/src/libsystemd-network/sd-lldp.c @@ -22,15 +22,19 @@ #include <arpa/inet.h> -#include "siphash24.h" -#include "hashmap.h" - -#include "lldp-tlv.h" -#include "lldp-port.h" #include "sd-lldp.h" -#include "prioq.h" + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "hashmap.h" #include "lldp-internal.h" +#include "lldp-port.h" +#include "lldp-tlv.h" #include "lldp-util.h" +#include "prioq.h" +#include "siphash24.h" +#include "string-util.h" typedef enum LLDPAgentRXState { LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4, diff --git a/src/systemd/src/libsystemd/sd-event/event-util.h b/src/systemd/src/libsystemd/sd-event/event-util.h index e7cad9be46..ae020340a5 100644 --- a/src/systemd/src/libsystemd/sd-event/event-util.h +++ b/src/systemd/src/libsystemd/sd-event/event-util.h @@ -21,9 +21,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" #include "sd-event.h" +#include "util.h" + DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event*, sd_event_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, sd_event_source_unref); diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c index eb539ad318..c12bb1e20b 100644 --- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c +++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c @@ -23,10 +23,14 @@ #include <fcntl.h> #include <unistd.h> -#include "util.h" -#include "macro.h" #include "sd-id128.h" + +#include "fd-util.h" +#include "hexdecoct.h" +#include "io-util.h" +#include "macro.h" #include "random-util.h" +#include "util.h" _public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) { unsigned n; diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c index 5680f01bd9..423ccca9cc 100644 --- a/src/systemd/src/shared/dns-domain.c +++ b/src/systemd/src/shared/dns-domain.c @@ -24,7 +24,11 @@ #include <stringprep.h> #endif +#include "alloc-util.h" #include "dns-domain.h" +#include "hexdecoct.h" +#include "parse-util.h" +#include "string-util.h" int dns_label_unescape(const char **name, char *dest, size_t sz) { const char *n; @@ -711,3 +715,37 @@ int dns_name_single_label(const char *name) { return r == 0 && *name == 0; } + +/* Encode a domain name according to RFC 1035 Section 3.1 */ +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) { + uint8_t *label_length; + uint8_t *out; + int r; + + assert_return(buffer, -EINVAL); + assert_return(domain, -EINVAL); + assert_return(domain[0], -EINVAL); + + out = buffer; + + do { + /* reserve a byte for label length */ + if (len == 0) + return -ENOBUFS; + len--; + label_length = out; + out++; + + /* convert and copy a single label */ + r = dns_label_unescape(&domain, (char *) out, len); + if (r < 0) + return r; + + /* fill label length, move forward */ + *label_length = r; + out += r; + len -= r; + } while (r != 0); + + return out - buffer; +} diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h index 1f0d242c18..b214897440 100644 --- a/src/systemd/src/shared/dns-domain.h +++ b/src/systemd/src/shared/dns-domain.h @@ -67,3 +67,5 @@ int dns_name_address(const char *p, int *family, union in_addr_union *a); int dns_name_root(const char *name); int dns_name_single_label(const char *name); + +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len); diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h index 4291fb7ebc..fc1d70e738 100644 --- a/src/systemd/src/systemd/sd-dhcp-client.h +++ b/src/systemd/src/systemd/sd-dhcp-client.h @@ -22,11 +22,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/in.h> +#include <inttypes.h> #include <net/ethernet.h> +#include <netinet/in.h> +#include <sys/types.h> -#include "sd-event.h" #include "sd-dhcp-lease.h" +#include "sd-event.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; enum { SD_DHCP_CLIENT_EVENT_STOP = 0, @@ -72,4 +78,6 @@ int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int pri int sd_dhcp_client_detach_event(sd_dhcp_client *client); sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client); +_SD_END_DECLARATIONS; + #endif diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h index ed5bceecdd..38222594e7 100644 --- a/src/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/src/systemd/sd-dhcp-lease.h @@ -23,8 +23,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/in.h> +#include <inttypes.h> #include <net/ethernet.h> +#include <netinet/in.h> +#include <sys/types.h> + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; typedef struct sd_dhcp_lease sd_dhcp_lease; struct sd_dhcp_route; @@ -52,4 +58,6 @@ int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, s int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len); int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); +_SD_END_DECLARATIONS; + #endif diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h index 90c35ef3f6..29e95e2492 100644 --- a/src/systemd/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/src/systemd/sd-dhcp6-client.h @@ -22,11 +22,16 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <inttypes.h> #include <net/ethernet.h> +#include <sys/types.h> +#include "sd-dhcp6-lease.h" #include "sd-event.h" -#include "sd-dhcp6-lease.h" +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; enum { SD_DHCP6_CLIENT_EVENT_STOP = 0, @@ -44,14 +49,13 @@ int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata); int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index); +int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address); int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, size_t addr_len, uint16_t arp_type); int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid, size_t duid_len); -int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, - bool enabled); -int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, - bool *enabled); +int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled); +int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled); int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option); @@ -59,6 +63,7 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret); int sd_dhcp6_client_stop(sd_dhcp6_client *client); int sd_dhcp6_client_start(sd_dhcp6_client *client); +int sd_dhcp6_client_is_running(sd_dhcp6_client *client); int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int priority); int sd_dhcp6_client_detach_event(sd_dhcp6_client *client); @@ -67,4 +72,6 @@ sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client); sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client); int sd_dhcp6_client_new(sd_dhcp6_client **ret); +_SD_END_DECLARATIONS; + #endif diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h index dc3df3bbf7..3fc0ee4bed 100644 --- a/src/systemd/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/src/systemd/sd-dhcp6-lease.h @@ -23,8 +23,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <inttypes.h> #include <netinet/in.h> +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + typedef struct sd_dhcp6_lease sd_dhcp6_lease; void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease); @@ -42,4 +47,6 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); +_SD_END_DECLARATIONS; + #endif diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index 565de5495a..fb97f7f28d 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -22,11 +22,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> -#include <sys/signalfd.h> -#include <sys/epoll.h> #include <inttypes.h> #include <signal.h> +#include <sys/epoll.h> +#include <sys/signalfd.h> +#include <sys/types.h> #include "_sd-common.h" @@ -56,7 +56,8 @@ enum { SD_EVENT_PENDING, SD_EVENT_RUNNING, SD_EVENT_EXITING, - SD_EVENT_FINISHED + SD_EVENT_FINISHED, + SD_EVENT_PREPARING, }; enum { @@ -87,9 +88,9 @@ int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callb int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_prepare(sd_event *e); -int sd_event_wait(sd_event *e, uint64_t timeout); +int sd_event_wait(sd_event *e, uint64_t usec); int sd_event_dispatch(sd_event *e); -int sd_event_run(sd_event *e, uint64_t timeout); +int sd_event_run(sd_event *e, uint64_t usec); int sd_event_loop(sd_event *e); int sd_event_exit(sd_event *e, int code); diff --git a/src/systemd/src/systemd/sd-icmp6-nd.h b/src/systemd/src/systemd/sd-icmp6-nd.h deleted file mode 100644 index cb6c24a0cb..0000000000 --- a/src/systemd/src/systemd/sd-icmp6-nd.h +++ /dev/null @@ -1,79 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foosdicmp6ndfoo -#define foosdicmp6ndfoo - -/*** - This file is part of systemd. - - Copyright (C) 2014 Intel Corporation. All rights reserved. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <net/ethernet.h> - -#include "sd-event.h" - -enum { - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE = 0, - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1, - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER = 2, - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3, - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED = 4, -}; - -typedef struct sd_icmp6_nd sd_icmp6_nd; - -typedef void(*sd_icmp6_nd_callback_t)(sd_icmp6_nd *nd, int event, - void *userdata); - -int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t cb, - void *userdata); -int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index); -int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr); - -int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority); -int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd); -sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd); - -sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd); -sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd); -int sd_icmp6_nd_new(sd_icmp6_nd **ret); - -int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen, - struct in6_addr *addr); - -int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu); -int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr, - uint8_t *prefixlen); -int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr, - uint8_t *prefixlen); - -int sd_icmp6_nd_stop(sd_icmp6_nd *nd); -int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd); - -#define SD_ICMP6_ND_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" - -#define SD_ICMP6_ND_ADDRESS_FORMAT_VAL(address) \ - be16toh((address).s6_addr16[0]), \ - be16toh((address).s6_addr16[1]), \ - be16toh((address).s6_addr16[2]), \ - be16toh((address).s6_addr16[3]), \ - be16toh((address).s6_addr16[4]), \ - be16toh((address).s6_addr16[5]), \ - be16toh((address).s6_addr16[6]), \ - be16toh((address).s6_addr16[7]) - -#endif diff --git a/src/systemd/src/systemd/sd-ipv4acd.h b/src/systemd/src/systemd/sd-ipv4acd.h index adcb2c7b92..c1e79640eb 100644 --- a/src/systemd/src/systemd/sd-ipv4acd.h +++ b/src/systemd/src/systemd/sd-ipv4acd.h @@ -23,12 +23,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> -#include <netinet/in.h> #include <net/ethernet.h> +#include <netinet/in.h> #include "sd-event.h" +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + enum { SD_IPV4ACD_EVENT_STOP = 0, SD_IPV4ACD_EVENT_BIND = 1, @@ -45,11 +48,13 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata); int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr); int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index); int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address); -bool sd_ipv4acd_is_running(sd_ipv4acd *ll); +int sd_ipv4acd_is_running(sd_ipv4acd *ll); int sd_ipv4acd_start(sd_ipv4acd *ll); int sd_ipv4acd_stop(sd_ipv4acd *ll); sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll); sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll); int sd_ipv4acd_new (sd_ipv4acd **ret); +_SD_END_DECLARATIONS; + #endif diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h index 677505f0c6..1d25f02bd0 100644 --- a/src/systemd/src/systemd/sd-ipv4ll.h +++ b/src/systemd/src/systemd/sd-ipv4ll.h @@ -22,12 +22,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> -#include <netinet/in.h> #include <net/ethernet.h> +#include <netinet/in.h> #include "sd-event.h" +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + enum { SD_IPV4LL_EVENT_STOP = 0, SD_IPV4LL_EVENT_BIND = 1, @@ -43,12 +46,15 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata); int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index); +int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address); int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed); -bool sd_ipv4ll_is_running(sd_ipv4ll *ll); +int sd_ipv4ll_is_running(sd_ipv4ll *ll); int sd_ipv4ll_start(sd_ipv4ll *ll); int sd_ipv4ll_stop(sd_ipv4ll *ll); sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll); sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll); int sd_ipv4ll_new (sd_ipv4ll **ret); +_SD_END_DECLARATIONS; + #endif diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h index 308d42c6e9..16d297a52d 100644 --- a/src/systemd/src/systemd/sd-lldp.h +++ b/src/systemd/src/systemd/sd-lldp.h @@ -1,5 +1,8 @@ /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ +#ifndef foosdlldphfoo +#define foosdlldphfoo + /*** This file is part of systemd. @@ -20,10 +23,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once +#include <inttypes.h> +#include <net/ethernet.h> #include "sd-event.h" +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + enum { SD_LLDP_EVENT_UPDATE_INFO = 0, }; @@ -35,7 +43,7 @@ enum { }; typedef struct sd_lldp sd_lldp; -typedef struct tlv_packet sd_lldp_packet; +typedef struct sd_lldp_packet sd_lldp_packet; typedef void (*sd_lldp_cb_t)(sd_lldp *lldp, int event, void *userdata); @@ -72,3 +80,7 @@ sd_lldp_packet *sd_lldp_packet_unref(sd_lldp_packet *tlv); int sd_lldp_packet_get_destination_type(sd_lldp_packet *tlv, int *dest); int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h new file mode 100644 index 0000000000..71e65d4425 --- /dev/null +++ b/src/systemd/src/systemd/sd-ndisc.h @@ -0,0 +1,84 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foosdndiscfoo +#define foosdndiscfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Intel Corporation. All rights reserved. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <inttypes.h> +#include <net/ethernet.h> + +#include "sd-event.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_NDISC_EVENT_STOP = 0, + SD_NDISC_EVENT_TIMEOUT = 1, +}; + +typedef struct sd_ndisc sd_ndisc; + +typedef void(*sd_ndisc_router_callback_t)(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata); +typedef void(*sd_ndisc_prefix_onlink_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime, void *userdata); +typedef void(*sd_ndisc_prefix_autonomous_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime_prefered, unsigned lifetime_valid, void *userdata); +typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, void *userdata); + +int sd_ndisc_set_callback(sd_ndisc *nd, + sd_ndisc_router_callback_t rcb, + sd_ndisc_prefix_onlink_callback_t plcb, + sd_ndisc_prefix_autonomous_callback_t pacb, + sd_ndisc_callback_t cb, + void *userdata); +int sd_ndisc_set_index(sd_ndisc *nd, int interface_index); +int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr); + +int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority); +int sd_ndisc_detach_event(sd_ndisc *nd); +sd_event *sd_ndisc_get_event(sd_ndisc *nd); + +sd_ndisc *sd_ndisc_ref(sd_ndisc *nd); +sd_ndisc *sd_ndisc_unref(sd_ndisc *nd); +int sd_ndisc_new(sd_ndisc **ret); + +int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu); + +int sd_ndisc_stop(sd_ndisc *nd); +int sd_ndisc_router_discovery_start(sd_ndisc *nd); + +#define SD_NDISC_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" + +#define SD_NDISC_ADDRESS_FORMAT_VAL(address) \ + be16toh((address).s6_addr16[0]), \ + be16toh((address).s6_addr16[1]), \ + be16toh((address).s6_addr16[2]), \ + be16toh((address).s6_addr16[3]), \ + be16toh((address).s6_addr16[4]), \ + be16toh((address).s6_addr16[5]), \ + be16toh((address).s6_addr16[6]), \ + be16toh((address).s6_addr16[7]) + +_SD_END_DECLARATIONS; + +#endif |